/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- *//* vim:set ts=4 sw=4 et cindent: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"nsSocketTransport2.h"#include"mozilla/Attributes.h"#include"mozilla/Telemetry.h"#include"nsIOService.h"#include"nsStreamUtils.h"#include"nsNetSegmentUtils.h"#include"nsNetAddr.h"#include"nsTransportUtils.h"#include"nsProxyInfo.h"#include"nsNetCID.h"#include"nsNetUtil.h"#include"nsAutoPtr.h"#include"nsCOMPtr.h"#include"plstr.h"#include"prerr.h"#include"NetworkActivityMonitor.h"#include"NSSErrorsService.h"#include"mozilla/dom/ToJSValue.h"#include"mozilla/net/NeckoChild.h"#include"nsThreadUtils.h"#include"nsISocketProviderService.h"#include"nsISocketProvider.h"#include"nsISSLSocketControl.h"#include"nsIPipe.h"#include"nsIClassInfoImpl.h"#include"nsURLHelper.h"#include"nsIDNSService.h"#include"nsIDNSRecord.h"#include"nsICancelable.h"#include"TCPFastOpenLayer.h"#include<algorithm>#include"nsPrintfCString.h"#include"xpcpublic.h"#if defined(XP_WIN)#include"ShutdownLayer.h"#endif/* Following inclusions required for keepalive config not supported by NSPR. */#include"private/pprio.h"#if defined(XP_WIN)#include<winsock2.h>#include<mstcpip.h>#elif defined(XP_UNIX)#include<errno.h>#include<netinet/tcp.h>#endif/* End keepalive config inclusions. */#define SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS 0#define UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS 1#define SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS 2#define UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS 3//-----------------------------------------------------------------------------staticNS_DEFINE_CID(kSocketProviderServiceCID,NS_SOCKETPROVIDERSERVICE_CID);staticNS_DEFINE_CID(kDNSServiceCID,NS_DNSSERVICE_CID);//-----------------------------------------------------------------------------namespacemozilla{namespacenet{classnsSocketEvent:publicRunnable{public:nsSocketEvent(nsSocketTransport*transport,uint32_ttype,nsresultstatus=NS_OK,nsISupports*param=nullptr):Runnable("net::nsSocketEvent"),mTransport(transport),mType(type),mStatus(status),mParam(param){}NS_IMETHODRun()override{mTransport->OnSocketEvent(mType,mStatus,mParam);returnNS_OK;}private:RefPtr<nsSocketTransport>mTransport;uint32_tmType;nsresultmStatus;nsCOMPtr<nsISupports>mParam;};//-----------------------------------------------------------------------------//#define TEST_CONNECT_ERRORS#ifdef TEST_CONNECT_ERRORS#include<stdlib.h>staticPRErrorCodeRandomizeConnectError(PRErrorCodecode){//// To test out these errors, load http://www.yahoo.com/. It should load// correctly despite the random occurrence of these errors.//intn=rand();if(n>RAND_MAX/2){struct{PRErrorCodeerr_code;constchar*err_name;}errors[]={//// These errors should be recoverable provided there is another// IP address in mDNSRecord.//{PR_CONNECT_REFUSED_ERROR,"PR_CONNECT_REFUSED_ERROR"},{PR_CONNECT_TIMEOUT_ERROR,"PR_CONNECT_TIMEOUT_ERROR"},//// This error will cause this socket transport to error out;// however, if the consumer is HTTP, then the HTTP transaction// should be restarted when this error occurs.//{PR_CONNECT_RESET_ERROR,"PR_CONNECT_RESET_ERROR"},};n=n%(sizeof(errors)/sizeof(errors[0]));code=errors[n].err_code;SOCKET_LOG(("simulating NSPR error %d [%s]\n",code,errors[n].err_name));}returncode;}#endif//-----------------------------------------------------------------------------nsresultErrorAccordingToNSPR(PRErrorCodeerrorCode){nsresultrv=NS_ERROR_FAILURE;switch(errorCode){casePR_WOULD_BLOCK_ERROR:rv=NS_BASE_STREAM_WOULD_BLOCK;break;casePR_CONNECT_ABORTED_ERROR:casePR_CONNECT_RESET_ERROR:rv=NS_ERROR_NET_RESET;break;casePR_END_OF_FILE_ERROR:// XXX document this correlationrv=NS_ERROR_NET_INTERRUPT;break;casePR_CONNECT_REFUSED_ERROR:// We lump the following NSPR codes in with PR_CONNECT_REFUSED_ERROR. We// could get better diagnostics by adding distinct XPCOM error codes for// each of these, but there are a lot of places in Gecko that check// specifically for NS_ERROR_CONNECTION_REFUSED, all of which would need to// be checked.casePR_NETWORK_UNREACHABLE_ERROR:casePR_HOST_UNREACHABLE_ERROR:casePR_ADDRESS_NOT_AVAILABLE_ERROR:// Treat EACCES as a soft error since (at least on Linux) connect() returns// EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.casePR_NO_ACCESS_RIGHTS_ERROR:rv=NS_ERROR_CONNECTION_REFUSED;break;casePR_ADDRESS_NOT_SUPPORTED_ERROR:rv=NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;break;casePR_IO_TIMEOUT_ERROR:casePR_CONNECT_TIMEOUT_ERROR:rv=NS_ERROR_NET_TIMEOUT;break;casePR_OUT_OF_MEMORY_ERROR:// These really indicate that the descriptor table filled up, or that the// kernel ran out of network buffers - but nobody really cares which part of// the system ran out of memory.casePR_PROC_DESC_TABLE_FULL_ERROR:casePR_SYS_DESC_TABLE_FULL_ERROR:casePR_INSUFFICIENT_RESOURCES_ERROR:rv=NS_ERROR_OUT_OF_MEMORY;break;casePR_ADDRESS_IN_USE_ERROR:rv=NS_ERROR_SOCKET_ADDRESS_IN_USE;break;// These filename-related errors can arise when using Unix-domain sockets.casePR_FILE_NOT_FOUND_ERROR:rv=NS_ERROR_FILE_NOT_FOUND;break;casePR_IS_DIRECTORY_ERROR:rv=NS_ERROR_FILE_IS_DIRECTORY;break;casePR_LOOP_ERROR:rv=NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;break;casePR_NAME_TOO_LONG_ERROR:rv=NS_ERROR_FILE_NAME_TOO_LONG;break;casePR_NO_DEVICE_SPACE_ERROR:rv=NS_ERROR_FILE_NO_DEVICE_SPACE;break;casePR_NOT_DIRECTORY_ERROR:rv=NS_ERROR_FILE_NOT_DIRECTORY;break;casePR_READ_ONLY_FILESYSTEM_ERROR:rv=NS_ERROR_FILE_READ_ONLY;break;casePR_BAD_ADDRESS_ERROR:rv=NS_ERROR_UNKNOWN_HOST;break;default:if(psm::IsNSSErrorCode(errorCode)){rv=psm::GetXPCOMFromNSSError(errorCode);}break;// NSPR's socket code can return these, but they're not worth breaking out// into their own error codes, distinct from NS_ERROR_FAILURE://// PR_BAD_DESCRIPTOR_ERROR// PR_INVALID_ARGUMENT_ERROR// PR_NOT_SOCKET_ERROR// PR_NOT_TCP_SOCKET_ERROR// These would indicate a bug internal to the component.//// PR_PROTOCOL_NOT_SUPPORTED_ERROR// This means that we can't use the given "protocol" (like// IPPROTO_TCP or IPPROTO_UDP) with a socket of the given type. As// above, this indicates an internal bug.//// PR_IS_CONNECTED_ERROR// This indicates that we've applied a system call like 'bind' or// 'connect' to a socket that is already connected. The socket// components manage each file descriptor's state, and in some cases// handle this error result internally. We shouldn't be returning// this to our callers.//// PR_IO_ERROR// This is so vague that NS_ERROR_FAILURE is just as good.}SOCKET_LOG(("ErrorAccordingToNSPR [in=%d out=%"PRIx32"]\n",errorCode,static_cast<uint32_t>(rv)));returnrv;}//-----------------------------------------------------------------------------// socket input stream impl //-----------------------------------------------------------------------------nsSocketInputStream::nsSocketInputStream(nsSocketTransport*trans):mTransport(trans),mReaderRefCnt(0),mCondition(NS_OK),mCallbackFlags(0),mByteCount(0){}nsSocketInputStream::~nsSocketInputStream(){}// called on the socket transport thread...//// condition : failure code if socket has been closed//voidnsSocketInputStream::OnSocketReady(nsresultcondition){SOCKET_LOG(("nsSocketInputStream::OnSocketReady [this=%p cond=%"PRIx32"]\n",this,static_cast<uint32_t>(condition)));MOZ_ASSERT(OnSocketThread(),"not on socket thread");nsCOMPtr<nsIInputStreamCallback>callback;{MutexAutoLocklock(mTransport->mLock);// update condition, but be careful not to erase an already// existing error condition.if(NS_SUCCEEDED(mCondition))mCondition=condition;// ignore event if only waiting for closure and not closed.if(NS_FAILED(mCondition)||!(mCallbackFlags&WAIT_CLOSURE_ONLY)){callback=mCallback;mCallback=nullptr;mCallbackFlags=0;}}if(callback)callback->OnInputStreamReady(this);}NS_IMPL_QUERY_INTERFACE(nsSocketInputStream,nsIInputStream,nsIAsyncInputStream)NS_IMETHODIMP_(MozExternalRefCountType)nsSocketInputStream::AddRef(){++mReaderRefCnt;returnmTransport->AddRef();}NS_IMETHODIMP_(MozExternalRefCountType)nsSocketInputStream::Release(){if(--mReaderRefCnt==0)Close();returnmTransport->Release();}NS_IMETHODIMPnsSocketInputStream::Close(){returnCloseWithStatus(NS_BASE_STREAM_CLOSED);}NS_IMETHODIMPnsSocketInputStream::Available(uint64_t*avail){SOCKET_LOG(("nsSocketInputStream::Available [this=%p]\n",this));*avail=0;PRFileDesc*fd;{MutexAutoLocklock(mTransport->mLock);if(NS_FAILED(mCondition))returnmCondition;fd=mTransport->GetFD_Locked();if(!fd)returnNS_OK;}// cannot hold lock while calling NSPR. (worried about the fact that PSM// synchronously proxies notifications over to the UI thread, which could// mistakenly try to re-enter this code.)int32_tn=PR_Available(fd);// PSM does not implement PR_Available() so do a best approximation of it// with MSG_PEEKif((n==-1)&&(PR_GetError()==PR_NOT_IMPLEMENTED_ERROR)){charc;n=PR_Recv(fd,&c,1,PR_MSG_PEEK,0);SOCKET_LOG(("nsSocketInputStream::Available [this=%p] ""using PEEK backup n=%d]\n",this,n));}nsresultrv;{MutexAutoLocklock(mTransport->mLock);mTransport->ReleaseFD_Locked(fd);if(n>=0)*avail=n;else{PRErrorCodecode=PR_GetError();if(code==PR_WOULD_BLOCK_ERROR)returnNS_OK;mCondition=ErrorAccordingToNSPR(code);}rv=mCondition;}if(NS_FAILED(rv))mTransport->OnInputClosed(rv);returnrv;}NS_IMETHODIMPnsSocketInputStream::Read(char*buf,uint32_tcount,uint32_t*countRead){SOCKET_LOG(("nsSocketInputStream::Read [this=%p count=%u]\n",this,count));*countRead=0;PRFileDesc*fd=nullptr;{MutexAutoLocklock(mTransport->mLock);if(NS_FAILED(mCondition))return(mCondition==NS_BASE_STREAM_CLOSED)?NS_OK:mCondition;fd=mTransport->GetFD_Locked();if(!fd)returnNS_BASE_STREAM_WOULD_BLOCK;}SOCKET_LOG((" calling PR_Read [count=%u]\n",count));// cannot hold lock while calling NSPR. (worried about the fact that PSM// synchronously proxies notifications over to the UI thread, which could// mistakenly try to re-enter this code.)int32_tn=PR_Read(fd,buf,count);SOCKET_LOG((" PR_Read returned [n=%d]\n",n));nsresultrv=NS_OK;{MutexAutoLocklock(mTransport->mLock);#ifdef ENABLE_SOCKET_TRACINGif(n>0)mTransport->TraceInBuf(buf,n);#endifmTransport->ReleaseFD_Locked(fd);if(n>0)mByteCount+=(*countRead=n);elseif(n<0){PRErrorCodecode=PR_GetError();if(code==PR_WOULD_BLOCK_ERROR)returnNS_BASE_STREAM_WOULD_BLOCK;mCondition=ErrorAccordingToNSPR(code);}rv=mCondition;}if(NS_FAILED(rv))mTransport->OnInputClosed(rv);// only send this notification if we have indeed read some data.// see bug 196827 for an example of why this is important.if(n>0)mTransport->SendStatus(NS_NET_STATUS_RECEIVING_FROM);returnrv;}NS_IMETHODIMPnsSocketInputStream::ReadSegments(nsWriteSegmentFunwriter,void*closure,uint32_tcount,uint32_t*countRead){// socket stream is unbufferedreturnNS_ERROR_NOT_IMPLEMENTED;}NS_IMETHODIMPnsSocketInputStream::IsNonBlocking(bool*nonblocking){*nonblocking=true;returnNS_OK;}NS_IMETHODIMPnsSocketInputStream::CloseWithStatus(nsresultreason){SOCKET_LOG(("nsSocketInputStream::CloseWithStatus [this=%p reason=%"PRIx32"]\n",this,static_cast<uint32_t>(reason)));// may be called from any threadnsresultrv;{MutexAutoLocklock(mTransport->mLock);if(NS_SUCCEEDED(mCondition))rv=mCondition=reason;elserv=NS_OK;}if(NS_FAILED(rv))mTransport->OnInputClosed(rv);returnNS_OK;}NS_IMETHODIMPnsSocketInputStream::AsyncWait(nsIInputStreamCallback*callback,uint32_tflags,uint32_tamount,nsIEventTarget*target){SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%p]\n",this));boolhasError=false;{MutexAutoLocklock(mTransport->mLock);if(callback&&target){//// build event proxy//mCallback=NS_NewInputStreamReadyEvent("nsSocketInputStream::AsyncWait",callback,target);}elsemCallback=callback;mCallbackFlags=flags;hasError=NS_FAILED(mCondition);}// unlock mTransport->mLockif(hasError){// OnSocketEvent will call OnInputStreamReady with an error code after// going through the event loop. We do this because most socket callers// do not expect AsyncWait() to synchronously execute the OnInputStreamReady// callback.mTransport->PostEvent(nsSocketTransport::MSG_INPUT_PENDING);}else{mTransport->OnInputPending();}returnNS_OK;}//-----------------------------------------------------------------------------// socket output stream impl //-----------------------------------------------------------------------------nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport*trans):mTransport(trans),mWriterRefCnt(0),mCondition(NS_OK),mCallbackFlags(0),mByteCount(0){}nsSocketOutputStream::~nsSocketOutputStream(){}// called on the socket transport thread...//// condition : failure code if socket has been closed//voidnsSocketOutputStream::OnSocketReady(nsresultcondition){SOCKET_LOG(("nsSocketOutputStream::OnSocketReady [this=%p cond=%"PRIx32"]\n",this,static_cast<uint32_t>(condition)));MOZ_ASSERT(OnSocketThread(),"not on socket thread");nsCOMPtr<nsIOutputStreamCallback>callback;{MutexAutoLocklock(mTransport->mLock);// update condition, but be careful not to erase an already// existing error condition.if(NS_SUCCEEDED(mCondition))mCondition=condition;// ignore event if only waiting for closure and not closed.if(NS_FAILED(mCondition)||!(mCallbackFlags&WAIT_CLOSURE_ONLY)){callback=mCallback;mCallback=nullptr;mCallbackFlags=0;}}if(callback)callback->OnOutputStreamReady(this);}NS_IMPL_QUERY_INTERFACE(nsSocketOutputStream,nsIOutputStream,nsIAsyncOutputStream)NS_IMETHODIMP_(MozExternalRefCountType)nsSocketOutputStream::AddRef(){++mWriterRefCnt;returnmTransport->AddRef();}NS_IMETHODIMP_(MozExternalRefCountType)nsSocketOutputStream::Release(){if(--mWriterRefCnt==0)Close();returnmTransport->Release();}NS_IMETHODIMPnsSocketOutputStream::Close(){returnCloseWithStatus(NS_BASE_STREAM_CLOSED);}NS_IMETHODIMPnsSocketOutputStream::Flush(){returnNS_OK;}NS_IMETHODIMPnsSocketOutputStream::Write(constchar*buf,uint32_tcount,uint32_t*countWritten){SOCKET_LOG(("nsSocketOutputStream::Write [this=%p count=%u]\n",this,count));*countWritten=0;// A write of 0 bytes can be used to force the initial SSL handshake, so do// not reject that.PRFileDesc*fd=nullptr;boolfastOpenInProgress;{MutexAutoLocklock(mTransport->mLock);if(NS_FAILED(mCondition))returnmCondition;fd=mTransport->GetFD_LockedAlsoDuringFastOpen();if(!fd)returnNS_BASE_STREAM_WOULD_BLOCK;fastOpenInProgress=mTransport->FastOpenInProgress();}if(fastOpenInProgress){// If we are in the fast open phase, we should not write more data// than TCPFastOpenLayer can accept. If we write more data, this data// will be buffered in tls and we want to avoid that.uint32_tavailableSpace=TCPFastOpenGetBufferSizeLeft(fd);count=(count>availableSpace)?availableSpace:count;if(!count){{MutexAutoLocklock(mTransport->mLock);mTransport->ReleaseFD_Locked(fd);}returnNS_BASE_STREAM_WOULD_BLOCK;}}SOCKET_LOG((" calling PR_Write [count=%u]\n",count));// cannot hold lock while calling NSPR. (worried about the fact that PSM// synchronously proxies notifications over to the UI thread, which could// mistakenly try to re-enter this code.)int32_tn=PR_Write(fd,buf,count);SOCKET_LOG((" PR_Write returned [n=%d]\n",n));nsresultrv=NS_OK;{MutexAutoLocklock(mTransport->mLock);#ifdef ENABLE_SOCKET_TRACINGif(n>0)mTransport->TraceOutBuf(buf,n);#endifmTransport->ReleaseFD_Locked(fd);if(n>0)mByteCount+=(*countWritten=n);elseif(n<0){PRErrorCodecode=PR_GetError();if(code==PR_WOULD_BLOCK_ERROR)returnNS_BASE_STREAM_WOULD_BLOCK;mCondition=ErrorAccordingToNSPR(code);}rv=mCondition;}if(NS_FAILED(rv))mTransport->OnOutputClosed(rv);// only send this notification if we have indeed written some data.// see bug 196827 for an example of why this is important.// During a fast open we are actually not sending data, the data will be// only buffered in the TCPFastOpenLayer. Therefore we will call// SendStatus(NS_NET_STATUS_SENDING_TO) when we really send data (i.e. when// TCPFastOpenFinish is called.if((n>0)&&!fastOpenInProgress){mTransport->SendStatus(NS_NET_STATUS_SENDING_TO);}returnrv;}NS_IMETHODIMPnsSocketOutputStream::WriteSegments(nsReadSegmentFunreader,void*closure,uint32_tcount,uint32_t*countRead){// socket stream is unbufferedreturnNS_ERROR_NOT_IMPLEMENTED;}nsresultnsSocketOutputStream::WriteFromSegments(nsIInputStream*input,void*closure,constchar*fromSegment,uint32_toffset,uint32_tcount,uint32_t*countRead){nsSocketOutputStream*self=(nsSocketOutputStream*)closure;returnself->Write(fromSegment,count,countRead);}NS_IMETHODIMPnsSocketOutputStream::WriteFrom(nsIInputStream*stream,uint32_tcount,uint32_t*countRead){returnstream->ReadSegments(WriteFromSegments,this,count,countRead);}NS_IMETHODIMPnsSocketOutputStream::IsNonBlocking(bool*nonblocking){*nonblocking=true;returnNS_OK;}NS_IMETHODIMPnsSocketOutputStream::CloseWithStatus(nsresultreason){SOCKET_LOG(("nsSocketOutputStream::CloseWithStatus [this=%p reason=%"PRIx32"]\n",this,static_cast<uint32_t>(reason)));// may be called from any threadnsresultrv;{MutexAutoLocklock(mTransport->mLock);if(NS_SUCCEEDED(mCondition))rv=mCondition=reason;elserv=NS_OK;}if(NS_FAILED(rv))mTransport->OnOutputClosed(rv);returnNS_OK;}NS_IMETHODIMPnsSocketOutputStream::AsyncWait(nsIOutputStreamCallback*callback,uint32_tflags,uint32_tamount,nsIEventTarget*target){SOCKET_LOG(("nsSocketOutputStream::AsyncWait [this=%p]\n",this));{MutexAutoLocklock(mTransport->mLock);if(callback&&target){//// build event proxy//mCallback=NS_NewOutputStreamReadyEvent(callback,target);}elsemCallback=callback;mCallbackFlags=flags;}mTransport->OnOutputPending();returnNS_OK;}//-----------------------------------------------------------------------------// socket transport impl//-----------------------------------------------------------------------------nsSocketTransport::nsSocketTransport():mTypes(nullptr),mTypeCount(0),mPort(0),mProxyPort(0),mOriginPort(0),mProxyTransparent(false),mProxyTransparentResolvesHost(false),mHttpsProxy(false),mConnectionFlags(0),mReuseAddrPort(false),mState(STATE_CLOSED),mAttached(false),mInputClosed(true),mOutputClosed(true),mResolving(false),mNetAddrIsSet(false),mSelfAddrIsSet(false),mNetAddrPreResolved(false),mLock("nsSocketTransport.mLock"),mFD(this),mFDref(0),mFDconnected(false),mFDFastOpenInProgress(false),mSocketTransportService(gSocketTransportService),mInput(this),mOutput(this),mQoSBits(0x00),mKeepaliveEnabled(false),mKeepaliveIdleTimeS(-1),mKeepaliveRetryIntervalS(-1),mKeepaliveProbeCount(-1),mFastOpenCallback(nullptr),mFastOpenLayerHasBufferedData(false),mDoNotRetryToConnect(false){SOCKET_LOG(("creating nsSocketTransport @%p\n",this));mTimeouts[TIMEOUT_CONNECT]=UINT16_MAX;// no timeoutmTimeouts[TIMEOUT_READ_WRITE]=UINT16_MAX;// no timeout}nsSocketTransport::~nsSocketTransport(){SOCKET_LOG(("destroying nsSocketTransport @%p\n",this));CleanupTypes();}voidnsSocketTransport::CleanupTypes(){// cleanup socket type infoif(mTypes){for(uint32_ti=0;i<mTypeCount;++i){PL_strfree(mTypes[i]);}free(mTypes);mTypes=nullptr;}mTypeCount=0;}nsresultnsSocketTransport::Init(constchar**types,uint32_ttypeCount,constnsACString&host,uint16_tport,constnsACString&hostRoute,uint16_tportRoute,nsIProxyInfo*givenProxyInfo){nsCOMPtr<nsProxyInfo>proxyInfo;if(givenProxyInfo){proxyInfo=do_QueryInterface(givenProxyInfo);NS_ENSURE_ARG(proxyInfo);}// init socket type infomOriginHost=host;mOriginPort=port;if(!hostRoute.IsEmpty()){mHost=hostRoute;mPort=portRoute;}else{mHost=host;mPort=port;}if(proxyInfo){mHttpsProxy=proxyInfo->IsHTTPS();}constchar*proxyType=nullptr;mProxyInfo=proxyInfo;if(proxyInfo){mProxyPort=proxyInfo->Port();mProxyHost=proxyInfo->Host();// grab proxy type (looking for "socks" for example)proxyType=proxyInfo->Type();if(proxyType&&(proxyInfo->IsHTTP()||proxyInfo->IsHTTPS()||proxyInfo->IsDirect()||!strcmp(proxyType,"unknown"))){proxyType=nullptr;}}SOCKET_LOG(("nsSocketTransport::Init [this=%p host=%s:%hu origin=%s:%d proxy=%s:%hu]\n",this,mHost.get(),mPort,mOriginHost.get(),mOriginPort,mProxyHost.get(),mProxyPort));// include proxy type as a socket type if proxy type is not "http"mTypeCount=typeCount+(proxyType!=nullptr);if(!mTypeCount)returnNS_OK;// if we have socket types, then the socket provider service had// better exist!nsresultrv;nsCOMPtr<nsISocketProviderService>spserv=do_GetService(kSocketProviderServiceCID,&rv);if(NS_FAILED(rv))returnrv;mTypes=(char**)malloc(mTypeCount*sizeof(char*));if(!mTypes)returnNS_ERROR_OUT_OF_MEMORY;// now verify that each socket type has a registered socket provider.for(uint32_ti=0,type=0;i<mTypeCount;++i){// store socket typesif(i==0&&proxyType)mTypes[i]=PL_strdup(proxyType);elsemTypes[i]=PL_strdup(types[type++]);if(!mTypes[i]){mTypeCount=i;returnNS_ERROR_OUT_OF_MEMORY;}nsCOMPtr<nsISocketProvider>provider;rv=spserv->GetSocketProvider(mTypes[i],getter_AddRefs(provider));if(NS_FAILED(rv)){NS_WARNING("no registered socket provider");returnrv;}// note if socket type corresponds to a transparent proxy// XXX don't hardcode SOCKS here (use proxy info's flags instead).if((strcmp(mTypes[i],"socks")==0)||(strcmp(mTypes[i],"socks4")==0)){mProxyTransparent=true;if(proxyInfo->Flags()&nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST){// we want the SOCKS layer to send the hostname// and port to the proxy and let it do the DNS.mProxyTransparentResolvesHost=true;}}}returnNS_OK;}nsresultnsSocketTransport::InitPreResolved(constchar**socketTypes,uint32_ttypeCount,constnsACString&host,uint16_tport,constnsACString&hostRoute,uint16_tportRoute,nsIProxyInfo*proxyInfo,constmozilla::net::NetAddr*addr){nsresultrv=Init(socketTypes,typeCount,host,port,hostRoute,portRoute,proxyInfo);if(NS_WARN_IF(NS_FAILED(rv))){returnrv;}mNetAddr=*addr;mNetAddrPreResolved=true;returnNS_OK;}nsresultnsSocketTransport::InitWithFilename(constchar*filename){#if defined(XP_UNIX)size_tfilenameLength=strlen(filename);if(filenameLength>sizeof(mNetAddr.local.path)-1)returnNS_ERROR_FILE_NAME_TOO_LONG;mHost.Assign(filename);mPort=0;mTypeCount=0;mNetAddr.local.family=AF_LOCAL;memcpy(mNetAddr.local.path,filename,filenameLength);mNetAddr.local.path[filenameLength]='\0';mNetAddrIsSet=true;returnNS_OK;#elsereturnNS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;#endif}nsresultnsSocketTransport::InitWithConnectedSocket(PRFileDesc*fd,constNetAddr*addr){MOZ_ASSERT(OnSocketThread(),"not on socket thread");NS_ASSERTION(!mFD.IsInitialized(),"already initialized");charbuf[kNetAddrMaxCStrBufSize];NetAddrToString(addr,buf,sizeof(buf));mHost.Assign(buf);uint16_tport;if(addr->raw.family==AF_INET)port=addr->inet.port;elseif(addr->raw.family==AF_INET6)port=addr->inet6.port;elseport=0;mPort=ntohs(port);memcpy(&mNetAddr,addr,sizeof(NetAddr));mPollFlags=(PR_POLL_READ|PR_POLL_WRITE|PR_POLL_EXCEPT);mPollTimeout=mTimeouts[TIMEOUT_READ_WRITE];mState=STATE_TRANSFERRING;SetSocketName(fd);mNetAddrIsSet=true;{MutexAutoLocklock(mLock);mFD=fd;mFDref=1;mFDconnected=1;}// make sure new socket is non-blockingPRSocketOptionDataopt;opt.option=PR_SockOpt_Nonblocking;opt.value.non_blocking=true;PR_SetSocketOption(fd,&opt);SOCKET_LOG(("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",this,mHost.get(),mPort));// jump to InitiateSocket to get ourselves attached to the STS poll list.returnPostEvent(MSG_RETRY_INIT_SOCKET);}nsresultnsSocketTransport::InitWithConnectedSocket(PRFileDesc*aFD,constNetAddr*aAddr,nsISupports*aSecInfo){mSecInfo=aSecInfo;returnInitWithConnectedSocket(aFD,aAddr);}nsresultnsSocketTransport::PostEvent(uint32_ttype,nsresultstatus,nsISupports*param){SOCKET_LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%"PRIx32" param=%p]\n",this,type,static_cast<uint32_t>(status),param));nsCOMPtr<nsIRunnable>event=newnsSocketEvent(this,type,status,param);if(!event)returnNS_ERROR_OUT_OF_MEMORY;returnmSocketTransportService->Dispatch(event,NS_DISPATCH_NORMAL);}voidnsSocketTransport::SendStatus(nsresultstatus){SOCKET_LOG(("nsSocketTransport::SendStatus [this=%p status=%"PRIx32"]\n",this,static_cast<uint32_t>(status)));nsCOMPtr<nsITransportEventSink>sink;uint64_tprogress;{MutexAutoLocklock(mLock);sink=mEventSink;switch(status){caseNS_NET_STATUS_SENDING_TO:progress=mOutput.ByteCount();// If Fast Open is used, we buffer some data in TCPFastOpenLayer,// This data can be only tls data or application data as well.// socketTransport should send status only if it really has sent// application data. socketTransport cannot query transaction for// that info but it can know if transaction has send data if // mOutput.ByteCount() is > 0.if(progress==0){return;}break;caseNS_NET_STATUS_RECEIVING_FROM:progress=mInput.ByteCount();break;default:progress=0;break;}}if(sink){sink->OnTransportStatus(this,status,progress,-1);}}nsresultnsSocketTransport::ResolveHost(){SOCKET_LOG(("nsSocketTransport::ResolveHost [this=%p %s:%d%s]\n",this,SocketHost().get(),SocketPort(),mConnectionFlags&nsSocketTransport::BYPASS_CACHE?" bypass cache":""));nsresultrv;if(mNetAddrPreResolved){mState=STATE_RESOLVING;returnPostEvent(MSG_DNS_LOOKUP_COMPLETE,NS_OK,nullptr);}if(!mProxyHost.IsEmpty()){if(!mProxyTransparent||mProxyTransparentResolvesHost){#if defined(XP_UNIX)MOZ_ASSERT(!mNetAddrIsSet||mNetAddr.raw.family!=AF_LOCAL,"Unix domain sockets can't be used with proxies");#endif// When not resolving mHost locally, we still want to ensure that// it only contains valid characters. See bug 304904 for details.// Sometimes the end host is not yet known and mHost is *if(!net_IsValidHostName(mHost)&&!mHost.Equals(NS_LITERAL_CSTRING("*"))){SOCKET_LOG((" invalid hostname %s\n",mHost.get()));returnNS_ERROR_UNKNOWN_HOST;}}if(mProxyTransparentResolvesHost){// Name resolution is done on the server side. Just pretend// client resolution is complete, this will get picked up later.// since we don't need to do DNS now, we bypass the resolving// step by initializing mNetAddr to an empty address, but we// must keep the port. The SOCKS IO layer will use the hostname// we send it when it's created, rather than the empty address// we send with the connect call.mState=STATE_RESOLVING;mNetAddr.raw.family=AF_INET;mNetAddr.inet.port=htons(SocketPort());mNetAddr.inet.ip=htonl(INADDR_ANY);returnPostEvent(MSG_DNS_LOOKUP_COMPLETE,NS_OK,nullptr);}}nsCOMPtr<nsIDNSService>dns=do_GetService(kDNSServiceCID,&rv);if(NS_FAILED(rv))returnrv;mResolving=true;uint32_tdnsFlags=0;if(mConnectionFlags&nsSocketTransport::BYPASS_CACHE)dnsFlags=nsIDNSService::RESOLVE_BYPASS_CACHE;if(mConnectionFlags&nsSocketTransport::DISABLE_IPV6)dnsFlags|=nsIDNSService::RESOLVE_DISABLE_IPV6;if(mConnectionFlags&nsSocketTransport::DISABLE_IPV4)dnsFlags|=nsIDNSService::RESOLVE_DISABLE_IPV4;NS_ASSERTION(!(dnsFlags&nsIDNSService::RESOLVE_DISABLE_IPV6)||!(dnsFlags&nsIDNSService::RESOLVE_DISABLE_IPV4),"Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");SendStatus(NS_NET_STATUS_RESOLVING_HOST);if(!SocketHost().Equals(mOriginHost)){SOCKET_LOG(("nsSocketTransport %p origin %s doing dns for %s\n",this,mOriginHost.get(),SocketHost().get()));}rv=dns->AsyncResolveExtendedNative(SocketHost(),dnsFlags,mNetworkInterfaceId,this,nullptr,mOriginAttributes,getter_AddRefs(mDNSRequest));if(NS_SUCCEEDED(rv)){SOCKET_LOG((" advancing to STATE_RESOLVING\n"));mState=STATE_RESOLVING;}returnrv;}nsresultnsSocketTransport::BuildSocket(PRFileDesc*&fd,bool&proxyTransparent,bool&usingSSL){SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%p]\n",this));nsresultrv;proxyTransparent=false;usingSSL=false;if(mTypeCount==0){fd=PR_OpenTCPSocket(mNetAddr.raw.family);rv=fd?NS_OK:NS_ERROR_OUT_OF_MEMORY;}else{#if defined(XP_UNIX)MOZ_ASSERT(!mNetAddrIsSet||mNetAddr.raw.family!=AF_LOCAL,"Unix domain sockets can't be used with socket types");#endiffd=nullptr;nsCOMPtr<nsISocketProviderService>spserv=do_GetService(kSocketProviderServiceCID,&rv);if(NS_FAILED(rv))returnrv;// by setting host to mOriginHost, instead of mHost we send the// SocketProvider (e.g. PSM) the origin hostname but can still do DNS// on an explicit alternate service host nameconstchar*host=mOriginHost.get();int32_tport=(int32_t)mOriginPort;nsCOMPtr<nsIProxyInfo>proxyInfo=mProxyInfo;uint32_tcontrolFlags=0;uint32_ti;for(i=0;i<mTypeCount;++i){nsCOMPtr<nsISocketProvider>provider;SOCKET_LOG((" pushing io layer [%u:%s]\n",i,mTypes[i]));rv=spserv->GetSocketProvider(mTypes[i],getter_AddRefs(provider));if(NS_FAILED(rv))break;if(mProxyTransparentResolvesHost)controlFlags|=nsISocketProvider::PROXY_RESOLVES_HOST;if(mConnectionFlags&nsISocketTransport::ANONYMOUS_CONNECT)controlFlags|=nsISocketProvider::ANONYMOUS_CONNECT;if(mConnectionFlags&nsISocketTransport::NO_PERMANENT_STORAGE)controlFlags|=nsISocketProvider::NO_PERMANENT_STORAGE;if(mConnectionFlags&nsISocketTransport::MITM_OK)controlFlags|=nsISocketProvider::MITM_OK;if(mConnectionFlags&nsISocketTransport::BE_CONSERVATIVE)controlFlags|=nsISocketProvider::BE_CONSERVATIVE;nsCOMPtr<nsISupports>secinfo;if(i==0){// if this is the first type, we'll want the // service to allocate a new socket// when https proxying we want to just connect to the proxy as if// it were the end host (i.e. expect the proxy's cert)rv=provider->NewSocket(mNetAddr.raw.family,mHttpsProxy?mProxyHost.get():host,mHttpsProxy?mProxyPort:port,proxyInfo,mOriginAttributes,controlFlags,&fd,getter_AddRefs(secinfo));if(NS_SUCCEEDED(rv)&&!fd){NS_NOTREACHED("NewSocket succeeded but failed to create a PRFileDesc");rv=NS_ERROR_UNEXPECTED;}}else{// the socket has already been allocated, // so we just want the service to add itself// to the stack (such as pushing an io layer)rv=provider->AddToSocket(mNetAddr.raw.family,host,port,proxyInfo,mOriginAttributes,controlFlags,fd,getter_AddRefs(secinfo));}// controlFlags = 0; not used below this point...if(NS_FAILED(rv))break;// if the service was ssl or starttls, we want to hold onto the socket infoboolisSSL=(strcmp(mTypes[i],"ssl")==0);if(isSSL||(strcmp(mTypes[i],"starttls")==0)){// remember security info and give notification callbacks to PSM...nsCOMPtr<nsIInterfaceRequestor>callbacks;{MutexAutoLocklock(mLock);mSecInfo=secinfo;callbacks=mCallbacks;SOCKET_LOG((" [secinfo=%p callbacks=%p]\n",mSecInfo.get(),mCallbacks.get()));}// don't call into PSM while holding mLock!!nsCOMPtr<nsISSLSocketControl>secCtrl(do_QueryInterface(secinfo));if(secCtrl)secCtrl->SetNotificationCallbacks(callbacks);// remember if socket type is SSL so we can ProxyStartSSL if need be.usingSSL=isSSL;}elseif((strcmp(mTypes[i],"socks")==0)||(strcmp(mTypes[i],"socks4")==0)){// since socks is transparent, any layers above// it do not have to worry about proxy stuffproxyInfo=nullptr;proxyTransparent=true;}}if(NS_FAILED(rv)){SOCKET_LOG((" error pushing io layer [%u:%s rv=%"PRIx32"]\n",i,mTypes[i],static_cast<uint32_t>(rv)));if(fd){CloseSocket(fd,mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());}}}returnrv;}nsresultnsSocketTransport::InitiateSocket(){SOCKET_LOG(("nsSocketTransport::InitiateSocket [this=%p]\n",this));nsresultrv;boolisLocal;IsLocal(&isLocal);if(gIOService->IsNetTearingDown()){returnNS_ERROR_ABORT;}if(gIOService->IsOffline()){if(!isLocal)returnNS_ERROR_OFFLINE;}elseif(!isLocal){#ifdef DEBUG// all IP networking has to be done from the parentif(NS_SUCCEEDED(mCondition)&&((mNetAddr.raw.family==AF_INET)||(mNetAddr.raw.family==AF_INET6))){MOZ_ASSERT(!IsNeckoChild());}#endifif(NS_SUCCEEDED(mCondition)&&xpc::AreNonLocalConnectionsDisabled()&&!(IsIPAddrAny(&mNetAddr)||IsIPAddrLocal(&mNetAddr))){nsAutoCStringipaddr;RefPtr<nsNetAddr>netaddr=newnsNetAddr(&mNetAddr);netaddr->GetAddress(ipaddr);fprintf_stderr(stderr,"FATAL ERROR: Non-local network connections are disabled and a connection ""attempt to %s (%s) was made.\nYou should only access hostnames ""available via the test networking proxy (if running mochitests) ""or from a test-specific httpd.js server (if running xpcshell tests). ""Browser services should be disabled or redirected to a local server.\n",mHost.get(),ipaddr.get());MOZ_CRASH("Attempting to connect to non-local address!");}}// Hosts/Proxy Hosts that are Local IP Literals should not be speculatively// connected - Bug 853423.if(mConnectionFlags&nsISocketTransport::DISABLE_RFC1918&&IsIPAddrLocal(&mNetAddr)){if(SOCKET_LOG_ENABLED()){nsAutoCStringnetAddrCString;netAddrCString.SetCapacity(kIPv6CStrBufSize);if(!NetAddrToString(&mNetAddr,netAddrCString.BeginWriting(),kIPv6CStrBufSize))netAddrCString=NS_LITERAL_CSTRING("<IP-to-string failed>");SOCKET_LOG(("nsSocketTransport::InitiateSocket skipping ""speculative connection for host [%s:%d] proxy ""[%s:%d] with Local IP address [%s]",mHost.get(),mPort,mProxyHost.get(),mProxyPort,netAddrCString.get()));}mCondition=NS_ERROR_CONNECTION_REFUSED;OnSocketDetached(nullptr);returnmCondition;}//// find out if it is going to be ok to attach another socket to the STS.// if not then we have to wait for the STS to tell us that it is ok.// the notification is asynchronous, which means that when we could be// in a race to call AttachSocket once notified. for this reason, when// we get notified, we just re-enter this function. as a result, we are// sure to ask again before calling AttachSocket. in this way we deal// with the race condition. though it isn't the most elegant solution,// it is far simpler than trying to build a system that would guarantee// FIFO ordering (which wouldn't even be that valuable IMO). see bug// 194402 for more info.//if(!mSocketTransportService->CanAttachSocket()){nsCOMPtr<nsIRunnable>event=newnsSocketEvent(this,MSG_RETRY_INIT_SOCKET);if(!event)returnNS_ERROR_OUT_OF_MEMORY;returnmSocketTransportService->NotifyWhenCanAttachSocket(event);}//// if we already have a connected socket, then just attach and return.//if(mFD.IsInitialized()){rv=mSocketTransportService->AttachSocket(mFD,this);if(NS_SUCCEEDED(rv))mAttached=true;returnrv;}//// create new socket fd, push io layers, etc.//PRFileDesc*fd;boolproxyTransparent;boolusingSSL;rv=BuildSocket(fd,proxyTransparent,usingSSL);if(NS_FAILED(rv)){SOCKET_LOG((" BuildSocket failed [rv=%"PRIx32"]\n",static_cast<uint32_t>(rv)));returnrv;}// Attach network activity monitorNetworkActivityMonitor::AttachIOLayer(fd);PRStatusstatus;// Make the socket non-blocking...PRSocketOptionDataopt;opt.option=PR_SockOpt_Nonblocking;opt.value.non_blocking=true;status=PR_SetSocketOption(fd,&opt);NS_ASSERTION(status==PR_SUCCESS,"unable to make socket non-blocking");if(mReuseAddrPort){SOCKET_LOG((" Setting port/addr reuse socket options\n"));// Set ReuseAddr for TCP sockets to enable having several// sockets bound to same local IP and portPRSocketOptionDataopt_reuseaddr;opt_reuseaddr.option=PR_SockOpt_Reuseaddr;opt_reuseaddr.value.reuse_addr=PR_TRUE;status=PR_SetSocketOption(fd,&opt_reuseaddr);if(status!=PR_SUCCESS){SOCKET_LOG((" Couldn't set reuse addr socket option: %d\n",status));}// And also set ReusePort for platforms supporting this socket optionPRSocketOptionDataopt_reuseport;opt_reuseport.option=PR_SockOpt_Reuseport;opt_reuseport.value.reuse_port=PR_TRUE;status=PR_SetSocketOption(fd,&opt_reuseport);if(status!=PR_SUCCESS&&PR_GetError()!=PR_OPERATION_NOT_SUPPORTED_ERROR){SOCKET_LOG((" Couldn't set reuse port socket option: %d\n",status));}}// disable the nagle algorithm - if we rely on it to coalesce writes into// full packets the final packet of a multi segment POST/PUT or pipeline// sequence is delayed a full rttopt.option=PR_SockOpt_NoDelay;opt.value.no_delay=true;PR_SetSocketOption(fd,&opt);// if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF// The Windows default of 8KB is too small and as of vista sp1, autotuning// only applies to receive windowint32_tsndBufferSize;mSocketTransportService->GetSendBufferSize(&sndBufferSize);if(sndBufferSize>0){opt.option=PR_SockOpt_SendBufferSize;opt.value.send_buffer_size=sndBufferSize;PR_SetSocketOption(fd,&opt);}if(mQoSBits){opt.option=PR_SockOpt_IpTypeOfService;opt.value.tos=mQoSBits;PR_SetSocketOption(fd,&opt);}#if defined(XP_WIN)// The linger is turned off by default. This is not a hard close, but// closesocket should return immediately and operating system tries to send// remaining data for certain, implementation specific, amount of time.// https://msdn.microsoft.com/en-us/library/ms739165.aspx//// Turn the linger option on an set the interval to 0. This will cause hard// close of the socket.opt.option=PR_SockOpt_Linger;opt.value.linger.polarity=1;opt.value.linger.linger=0;PR_SetSocketOption(fd,&opt);#endif// inform socket transport about this newly created socket...rv=mSocketTransportService->AttachSocket(fd,this);if(NS_FAILED(rv)){CloseSocket(fd,mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());returnrv;}mAttached=true;// assign mFD so that we can properly handle OnSocketDetached before we've// established a connection.{MutexAutoLocklock(mLock);mFD=fd;mFDref=1;mFDconnected=false;}SOCKET_LOG((" advancing to STATE_CONNECTING\n"));mState=STATE_CONNECTING;mPollTimeout=mTimeouts[TIMEOUT_CONNECT];SendStatus(NS_NET_STATUS_CONNECTING_TO);if(SOCKET_LOG_ENABLED()){charbuf[kNetAddrMaxCStrBufSize];NetAddrToString(&mNetAddr,buf,sizeof(buf));SOCKET_LOG((" trying address: %s\n",buf));}//// Initiate the connect() to the host...//PRNetAddrprAddr;{if(mBindAddr){MutexAutoLocklock(mLock);NetAddrToPRNetAddr(mBindAddr.get(),&prAddr);status=PR_Bind(fd,&prAddr);if(status!=PR_SUCCESS){returnNS_ERROR_FAILURE;}mBindAddr=nullptr;}}NetAddrToPRNetAddr(&mNetAddr,&prAddr);#ifdef XP_WIN// Find the real tcp socket and set non-blocking once again!// Bug 1158189.PRFileDesc*bottom=PR_GetIdentitiesLayer(fd,PR_NSPR_IO_LAYER);if(bottom){PROsfdosfd=PR_FileDesc2NativeHandle(bottom);u_longnonblocking=1;if(ioctlsocket(osfd,FIONBIO,&nonblocking)!=0){NS_WARNING("Socket could not be set non-blocking!");returnNS_ERROR_FAILURE;}}#endif// We use PRIntervalTime here because we need// nsIOService::LastOfflineStateChange time and// nsIOService::LastConectivityChange time to be atomic.PRIntervalTimeconnectStarted=0;if(gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()){connectStarted=PR_IntervalNow();}booltfo=false;if(mFastOpenCallback&&mFastOpenCallback->FastOpenEnabled()){if(NS_SUCCEEDED(AttachTCPFastOpenIOLayer(fd))){tfo=true;SOCKET_LOG(("nsSocketTransport::InitiateSocket TCP Fast Open ""started [this=%p]\n",this));}}boolconnectCalled=true;// This is only needed for telemetry.status=PR_Connect(fd,&prAddr,NS_SOCKET_CONNECT_TIMEOUT);PRErrorCodecode=PR_GetError();if((status==PR_SUCCESS)&&tfo){{MutexAutoLocklock(mLock);mFDFastOpenInProgress=true;}SOCKET_LOG(("Using TCP Fast Open."));rv=mFastOpenCallback->StartFastOpen();if(NS_FAILED(rv)){if(NS_SUCCEEDED(mCondition)){mCondition=rv;}mFastOpenCallback=nullptr;MutexAutoLocklock(mLock);mFDFastOpenInProgress=false;returnrv;}status=PR_FAILURE;connectCalled=false;boolfastOpenNotSupported=false;uint8_ttfoStatus=TFO_NOT_TRIED;TCPFastOpenFinish(fd,code,fastOpenNotSupported,tfoStatus);// If we have sent data, trigger a socket status event.if(tfoStatus==TFO_DATA_SENT){SendStatus(NS_NET_STATUS_SENDING_TO);}// If we have still some data buffered this data must be flush before// mOutput.OnSocketReady(NS_OK) is called in// nsSocketTransport::OnSocketReady, partially to keep socket status// event in order.mFastOpenLayerHasBufferedData=TCPFastOpenGetCurrentBufferSize(fd);mFastOpenCallback->SetFastOpenStatus(tfoStatus);SOCKET_LOG(("called StartFastOpen - code=%d; fastOpen is %s ""supported.\n",code,fastOpenNotSupported?"not":""));SOCKET_LOG(("TFO status %d\n",tfoStatus));if(fastOpenNotSupported){// When TCP_FastOpen is turned off on the local host// SendTo will return PR_NOT_TCP_SOCKET_ERROR. This is only// on Linux.// If a windows version does not support Fast Open, the return value// will be PR_NOT_IMPLEMENTED_ERROR. This is only for windows 10// versions older than version 1607, because we do not have subverion// to check, we need to call PR_SendTo to check if it is supported.mFastOpenCallback->FastOpenNotSupported();// FastOpenNotSupported will set Fast Open as not supported globally.// For this connection we will pretend that we still use fast open,// because of the fallback mechanism in case we need to restart the// attached transaction.connectCalled=true;}}else{mFastOpenCallback=nullptr;}if(gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()&&connectStarted&&connectCalled){SendPRBlockingTelemetry(connectStarted,Telemetry::PRCONNECT_BLOCKING_TIME_NORMAL,Telemetry::PRCONNECT_BLOCKING_TIME_SHUTDOWN,Telemetry::PRCONNECT_BLOCKING_TIME_CONNECTIVITY_CHANGE,Telemetry::PRCONNECT_BLOCKING_TIME_LINK_CHANGE,Telemetry::PRCONNECT_BLOCKING_TIME_OFFLINE);}if(status==PR_SUCCESS){// // we are connected!//OnSocketConnected();}else{#if defined(TEST_CONNECT_ERRORS)code=RandomizeConnectError(code);#endif//// If the PR_Connect(...) would block, then poll for a connection.//if((PR_WOULD_BLOCK_ERROR==code)||(PR_IN_PROGRESS_ERROR==code))mPollFlags=(PR_POLL_EXCEPT|PR_POLL_WRITE);//// If the socket is already connected, then return success...//elseif(PR_IS_CONNECTED_ERROR==code){//// we are connected!//OnSocketConnected();if(mSecInfo&&!mProxyHost.IsEmpty()&&proxyTransparent&&usingSSL){// if the connection phase is finished, and the ssl layer has// been pushed, and we were proxying (transparently; ie. nothing// has to happen in the protocol layer above us), it's time for// the ssl to start doing it's thing.nsCOMPtr<nsISSLSocketControl>secCtrl=do_QueryInterface(mSecInfo);if(secCtrl){SOCKET_LOG((" calling ProxyStartSSL()\n"));secCtrl->ProxyStartSSL();}// XXX what if we were forced to poll on the socket for a successful// connection... wouldn't we need to call ProxyStartSSL after a call// to PR_ConnectContinue indicates that we are connected?//// XXX this appears to be what the old socket transport did. why// isn't this broken?}}//// A SOCKS request was rejected; get the actual error code from// the OS error//elseif(PR_UNKNOWN_ERROR==code&&mProxyTransparent&&!mProxyHost.IsEmpty()){code=PR_GetOSError();rv=ErrorAccordingToNSPR(code);}//// The connection was refused...//else{if(gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()&&connectStarted&&connectStarted){SendPRBlockingTelemetry(connectStarted,Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_NORMAL,Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_SHUTDOWN,Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_CONNECTIVITY_CHANGE,Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_LINK_CHANGE,Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_OFFLINE);}rv=ErrorAccordingToNSPR(code);if((rv==NS_ERROR_CONNECTION_REFUSED)&&!mProxyHost.IsEmpty())rv=NS_ERROR_PROXY_CONNECTION_REFUSED;}}returnrv;}boolnsSocketTransport::RecoverFromError(){NS_ASSERTION(NS_FAILED(mCondition),"there should be something wrong");SOCKET_LOG(("nsSocketTransport::RecoverFromError [this=%p state=%x cond=%"PRIx32"]\n",this,mState,static_cast<uint32_t>(mCondition)));if(mDoNotRetryToConnect){SOCKET_LOG(("nsSocketTransport::RecoverFromError do not retry because ""mDoNotRetryToConnect is set [this=%p]\n",this));returnfalse;}#if defined(XP_UNIX)// Unix domain connections don't have multiple addresses to try,// so the recovery techniques here don't apply.if(mNetAddrIsSet&&mNetAddr.raw.family==AF_LOCAL)returnfalse;#endif// can only recover from errors in these statesif(mState!=STATE_RESOLVING&&mState!=STATE_CONNECTING)returnfalse;nsresultrv;// OK to check this outside mLockNS_ASSERTION(!mFDconnected,"socket should not be connected");// all connection failures need to be reported to DNS so that the next// time we will use a different address if available.// Skip conditions that can be cause by TCP Fast Open.if((!mFDFastOpenInProgress||((mCondition!=NS_ERROR_CONNECTION_REFUSED)&&(mCondition!=NS_ERROR_NET_TIMEOUT)&&(mCondition!=NS_ERROR_PROXY_CONNECTION_REFUSED)))&&mState==STATE_CONNECTING&&mDNSRecord){mDNSRecord->ReportUnusable(SocketPort());}// can only recover from these errorsif(mCondition!=NS_ERROR_CONNECTION_REFUSED&&mCondition!=NS_ERROR_PROXY_CONNECTION_REFUSED&&mCondition!=NS_ERROR_NET_TIMEOUT&&mCondition!=NS_ERROR_UNKNOWN_HOST&&mCondition!=NS_ERROR_UNKNOWN_PROXY_HOST)returnfalse;booltryAgain=false;if(mFDFastOpenInProgress&&((mCondition==NS_ERROR_CONNECTION_REFUSED)||(mCondition==NS_ERROR_NET_TIMEOUT)||(mCondition==NS_ERROR_PROXY_CONNECTION_REFUSED))){// TCP Fast Open can be blocked by middle boxes so we will retry// without it.tryAgain=true;// If we cancel the connection because backup socket was successfully// connected, mFDFastOpenInProgress will be true but mFastOpenCallback// will be nullptr.if(mFastOpenCallback){mFastOpenCallback->SetFastOpenConnected(mCondition,true);}mFastOpenCallback=nullptr;}else{if((mState==STATE_CONNECTING)&&mDNSRecord&&mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()){if(mNetAddr.raw.family==AF_INET){Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS);}elseif(mNetAddr.raw.family==AF_INET6){Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS);}}if(mConnectionFlags&(DISABLE_IPV6|DISABLE_IPV4)&&mCondition==NS_ERROR_UNKNOWN_HOST&&mState==STATE_RESOLVING&&!mProxyTransparentResolvesHost){SOCKET_LOG((" trying lookup again with both ipv4/ipv6 enabled\n"));mConnectionFlags&=~(DISABLE_IPV6|DISABLE_IPV4);tryAgain=true;}// try next ip address only if past the resolver stage...if(mState==STATE_CONNECTING&&mDNSRecord){nsresultrv=mDNSRecord->GetNextAddr(SocketPort(),&mNetAddr);if(NS_SUCCEEDED(rv)){SOCKET_LOG((" trying again with next ip address\n"));tryAgain=true;}elseif(mConnectionFlags&(DISABLE_IPV6|DISABLE_IPV4)){// Drop state to closed. This will trigger new round of DNS// resolving bellow.// XXX Could be optimized to only switch the flags to save// duplicate connection attempts.SOCKET_LOG((" failed to connect all ipv4-only or ipv6-only ""hosts, trying lookup/connect again with both ""ipv4/ipv6\n"));mState=STATE_CLOSED;mConnectionFlags&=~(DISABLE_IPV6|DISABLE_IPV4);tryAgain=true;}}}// prepare to try again.if(tryAgain){uint32_tmsg;if(mState==STATE_CONNECTING){mState=STATE_RESOLVING;msg=MSG_DNS_LOOKUP_COMPLETE;}else{mState=STATE_CLOSED;msg=MSG_ENSURE_CONNECT;}rv=PostEvent(msg,NS_OK);if(NS_FAILED(rv))tryAgain=false;}returntryAgain;}// called on the socket thread onlyvoidnsSocketTransport::OnMsgInputClosed(nsresultreason){SOCKET_LOG(("nsSocketTransport::OnMsgInputClosed [this=%p reason=%"PRIx32"]\n",this,static_cast<uint32_t>(reason)));MOZ_ASSERT(OnSocketThread(),"not on socket thread");mInputClosed=true;// check if event should affect entire transportif(NS_FAILED(reason)&&(reason!=NS_BASE_STREAM_CLOSED))mCondition=reason;// XXX except if NS_FAILED(mCondition), right??elseif(mOutputClosed)mCondition=NS_BASE_STREAM_CLOSED;// XXX except if NS_FAILED(mCondition), right??else{if(mState==STATE_TRANSFERRING)mPollFlags&=~PR_POLL_READ;mInput.OnSocketReady(reason);}}// called on the socket thread onlyvoidnsSocketTransport::OnMsgOutputClosed(nsresultreason){SOCKET_LOG(("nsSocketTransport::OnMsgOutputClosed [this=%p reason=%"PRIx32"]\n",this,static_cast<uint32_t>(reason)));MOZ_ASSERT(OnSocketThread(),"not on socket thread");mOutputClosed=true;// check if event should affect entire transportif(NS_FAILED(reason)&&(reason!=NS_BASE_STREAM_CLOSED))mCondition=reason;// XXX except if NS_FAILED(mCondition), right??elseif(mInputClosed)mCondition=NS_BASE_STREAM_CLOSED;// XXX except if NS_FAILED(mCondition), right??else{if(mState==STATE_TRANSFERRING)mPollFlags&=~PR_POLL_WRITE;mOutput.OnSocketReady(reason);}}voidnsSocketTransport::OnSocketConnected(){MOZ_ASSERT(OnSocketThread(),"not on socket thread");SOCKET_LOG((" advancing to STATE_TRANSFERRING\n"));mPollFlags=(PR_POLL_READ|PR_POLL_WRITE|PR_POLL_EXCEPT);mPollTimeout=mTimeouts[TIMEOUT_READ_WRITE];mState=STATE_TRANSFERRING;// Set the m*AddrIsSet flags only when state has reached TRANSFERRING// because we need to make sure its value does not change due to failovermNetAddrIsSet=true;if(mFDFastOpenInProgress&&mFastOpenCallback){// mFastOpenCallback can be null when for example h2 is negotiated on// another connection to the same host and all connections are// abandoned.mFastOpenCallback->SetFastOpenConnected(NS_OK,false);}mFastOpenCallback=nullptr;// assign mFD (must do this within the transport lock), but take care not// to trample over mFDref if mFD is already set.{MutexAutoLocklock(mLock);NS_ASSERTION(mFD.IsInitialized(),"no socket");NS_ASSERTION(mFDref==1,"wrong socket ref count");SetSocketName(mFD);mFDconnected=true;mFDFastOpenInProgress=false;}// Ensure keepalive is configured correctly if previously enabled.if(mKeepaliveEnabled){nsresultrv=SetKeepaliveEnabledInternal(true);if(NS_WARN_IF(NS_FAILED(rv))){SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%"PRIx32"]",static_cast<uint32_t>(rv)));}}SendStatus(NS_NET_STATUS_CONNECTED_TO);}voidnsSocketTransport::SetSocketName(PRFileDesc*fd){MOZ_ASSERT(OnSocketThread(),"not on socket thread");if(mSelfAddrIsSet){return;}PRNetAddrprAddr;memset(&prAddr,0,sizeof(prAddr));if(PR_GetSockName(fd,&prAddr)==PR_SUCCESS){PRNetAddrToNetAddr(&prAddr,&mSelfAddr);mSelfAddrIsSet=true;}}PRFileDesc*nsSocketTransport::GetFD_Locked(){mLock.AssertCurrentThreadOwns();// mFD is not available to the streams while disconnected.if(!mFDconnected)returnnullptr;if(mFD.IsInitialized())mFDref++;returnmFD;}PRFileDesc*nsSocketTransport::GetFD_LockedAlsoDuringFastOpen(){mLock.AssertCurrentThreadOwns();// mFD is not available to the streams while disconnected.if(!mFDconnected&&!mFDFastOpenInProgress){returnnullptr;}if(mFD.IsInitialized()){mFDref++;}returnmFD;}boolnsSocketTransport::FastOpenInProgress(){mLock.AssertCurrentThreadOwns();returnmFDFastOpenInProgress;}classThunkPRClose:publicRunnable{public:explicitThunkPRClose(PRFileDesc*fd):Runnable("net::ThunkPRClose"),mFD(fd){}NS_IMETHODRun()override{nsSocketTransport::CloseSocket(mFD,gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());returnNS_OK;}private:PRFileDesc*mFD;};voidSTS_PRCloseOnSocketTransport(PRFileDesc*fd){if(gSocketTransportService){// Can't PR_Close() a socket off STS thread. Thunk it to STS to diegSocketTransportService->Dispatch(newThunkPRClose(fd),NS_DISPATCH_NORMAL);}else{// something horrible has happenedNS_ASSERTION(gSocketTransportService,"No STS service");}}voidnsSocketTransport::ReleaseFD_Locked(PRFileDesc*fd){mLock.AssertCurrentThreadOwns();NS_ASSERTION(mFD==fd,"wrong fd");SOCKET_LOG(("JIMB: ReleaseFD_Locked: mFDref = %"PRIuPTR"\n",mFDref));if(--mFDref==0){if(gIOService->IsNetTearingDown()&&((PR_IntervalNow()-gIOService->NetTearingDownStarted())>gSocketTransportService->MaxTimeForPrClosePref())){// If shutdown last to long, let the socket leak and do not close it.SOCKET_LOG(("Intentional leak"));}elseif(OnSocketThread()){SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%p]\n",this));CloseSocket(mFD,mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());}else{// Can't PR_Close() a socket off STS thread. Thunk it to STS to dieSTS_PRCloseOnSocketTransport(mFD);}mFD=nullptr;}}//-----------------------------------------------------------------------------// socket event handler implvoidnsSocketTransport::OnSocketEvent(uint32_ttype,nsresultstatus,nsISupports*param){SOCKET_LOG(("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%"PRIx32" param=%p]\n",this,type,static_cast<uint32_t>(status),param));if(NS_FAILED(mCondition)){// block event since we're apparently already dead.SOCKET_LOG((" blocking event [condition=%"PRIx32"]\n",static_cast<uint32_t>(mCondition)));//// notify input/output streams in case either has a pending notify.//mInput.OnSocketReady(mCondition);mOutput.OnSocketReady(mCondition);return;}switch(type){caseMSG_ENSURE_CONNECT:SOCKET_LOG((" MSG_ENSURE_CONNECT\n"));//// ensure that we have created a socket, attached it, and have a// connection.//if(mState==STATE_CLOSED){// Unix domain sockets are ready to connect; mNetAddr is all we// need. Internet address families require a DNS lookup (or possibly// several) before we can connect.#if defined(XP_UNIX)if(mNetAddrIsSet&&mNetAddr.raw.family==AF_LOCAL)mCondition=InitiateSocket();else#endifmCondition=ResolveHost();}else{SOCKET_LOG((" ignoring redundant event\n"));}break;caseMSG_DNS_LOOKUP_COMPLETE:if(mDNSRequest)// only send this if we actually resolved anythingSendStatus(NS_NET_STATUS_RESOLVED_HOST);SOCKET_LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));mDNSRequest=nullptr;if(param){mDNSRecord=static_cast<nsIDNSRecord*>(param);mDNSRecord->GetNextAddr(SocketPort(),&mNetAddr);}// status contains DNS lookup statusif(NS_FAILED(status)){// When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP // proxy host is not found, so we fixup the error code.// For SOCKS proxies (mProxyTransparent == true), the socket // transport resolves the real host here, so there's no fixup // (see bug 226943).if((status==NS_ERROR_UNKNOWN_HOST)&&!mProxyTransparent&&!mProxyHost.IsEmpty())mCondition=NS_ERROR_UNKNOWN_PROXY_HOST;elsemCondition=status;}elseif(mState==STATE_RESOLVING){mCondition=InitiateSocket();}break;caseMSG_RETRY_INIT_SOCKET:mCondition=InitiateSocket();break;caseMSG_INPUT_CLOSED:SOCKET_LOG((" MSG_INPUT_CLOSED\n"));OnMsgInputClosed(status);break;caseMSG_INPUT_PENDING:SOCKET_LOG((" MSG_INPUT_PENDING\n"));OnMsgInputPending();break;caseMSG_OUTPUT_CLOSED:SOCKET_LOG((" MSG_OUTPUT_CLOSED\n"));OnMsgOutputClosed(status);break;caseMSG_OUTPUT_PENDING:SOCKET_LOG((" MSG_OUTPUT_PENDING\n"));OnMsgOutputPending();break;caseMSG_TIMEOUT_CHANGED:SOCKET_LOG((" MSG_TIMEOUT_CHANGED\n"));mPollTimeout=mTimeouts[(mState==STATE_TRANSFERRING)?TIMEOUT_READ_WRITE:TIMEOUT_CONNECT];break;default:SOCKET_LOG((" unhandled event!\n"));}if(NS_FAILED(mCondition)){SOCKET_LOG((" after event [this=%p cond=%"PRIx32"]\n",this,static_cast<uint32_t>(mCondition)));if(!mAttached)// need to process this error ourselves...OnSocketDetached(nullptr);}elseif(mPollFlags==PR_POLL_EXCEPT)mPollFlags=0;// make idle}//-----------------------------------------------------------------------------// socket handler implvoidnsSocketTransport::OnSocketReady(PRFileDesc*fd,int16_toutFlags){SOCKET_LOG(("nsSocketTransport::OnSocketReady [this=%p outFlags=%hd]\n",this,outFlags));if(outFlags==-1){SOCKET_LOG(("socket timeout expired\n"));mCondition=NS_ERROR_NET_TIMEOUT;return;}if((mState==STATE_TRANSFERRING)&&mFastOpenLayerHasBufferedData){// We have some data buffered in TCPFastOpenLayer. We will flush them// first. We need to do this first before calling OnSocketReady below// so that the socket status events are kept in the correct order.mFastOpenLayerHasBufferedData=TCPFastOpenFlushBuffer(fd);if(mFastOpenLayerHasBufferedData){return;}else{SendStatus(NS_NET_STATUS_SENDING_TO);}// If we are done sending the buffered data continue with the normal// path.// In case of an error, TCPFastOpenFlushBuffer will return false and// the normal code path will pick up the error.mFastOpenLayerHasBufferedData=false;}if(mState==STATE_TRANSFERRING){// if waiting to write and socket is writable or hit an exception.if((mPollFlags&PR_POLL_WRITE)&&(outFlags&~PR_POLL_READ)){// assume that we won't need to poll any longer (the stream will// request that we poll again if it is still pending).mPollFlags&=~PR_POLL_WRITE;mOutput.OnSocketReady(NS_OK);}// if waiting to read and socket is readable or hit an exception.if((mPollFlags&PR_POLL_READ)&&(outFlags&~PR_POLL_WRITE)){// assume that we won't need to poll any longer (the stream will// request that we poll again if it is still pending).mPollFlags&=~PR_POLL_READ;mInput.OnSocketReady(NS_OK);}// Update poll timeout in case it was changedmPollTimeout=mTimeouts[TIMEOUT_READ_WRITE];}elseif((mState==STATE_CONNECTING)&&!gIOService->IsNetTearingDown()){// We do not need to do PR_ConnectContinue when we are already// shutting down.// We use PRIntervalTime here because we need// nsIOService::LastOfflineStateChange time and// nsIOService::LastConectivityChange time to be atomic.PRIntervalTimeconnectStarted=0;if(gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()){connectStarted=PR_IntervalNow();}PRStatusstatus=PR_ConnectContinue(fd,outFlags);if(gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()&&connectStarted){SendPRBlockingTelemetry(connectStarted,Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_NORMAL,Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_SHUTDOWN,Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_CONNECTIVITY_CHANGE,Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_LINK_CHANGE,Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_OFFLINE);}if(status==PR_SUCCESS){//// we are connected!//OnSocketConnected();if(mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()){if(mNetAddr.raw.family==AF_INET){Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS);}elseif(mNetAddr.raw.family==AF_INET6){Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS);}}}else{PRErrorCodecode=PR_GetError();#if defined(TEST_CONNECT_ERRORS)code=RandomizeConnectError(code);#endif//// If the connect is still not ready, then continue polling...//if((PR_WOULD_BLOCK_ERROR==code)||(PR_IN_PROGRESS_ERROR==code)){// Set up the select flags for connect...mPollFlags=(PR_POLL_EXCEPT|PR_POLL_WRITE);// Update poll timeout in case it was changedmPollTimeout=mTimeouts[TIMEOUT_CONNECT];}//// The SOCKS proxy rejected our request. Find out why.//elseif(PR_UNKNOWN_ERROR==code&&mProxyTransparent&&!mProxyHost.IsEmpty()){code=PR_GetOSError();mCondition=ErrorAccordingToNSPR(code);}else{//// else, the connection failed...//mCondition=ErrorAccordingToNSPR(code);if((mCondition==NS_ERROR_CONNECTION_REFUSED)&&!mProxyHost.IsEmpty())mCondition=NS_ERROR_PROXY_CONNECTION_REFUSED;SOCKET_LOG((" connection failed! [reason=%"PRIx32"]\n",static_cast<uint32_t>(mCondition)));}}}elseif((mState==STATE_CONNECTING)&&gIOService->IsNetTearingDown()){// We do not need to do PR_ConnectContinue when we are already// shutting down.SOCKET_LOG(("We are in shutdown so skip PR_ConnectContinue and set ""and error.\n"));mCondition=NS_ERROR_ABORT;}else{NS_ERROR("unexpected socket state");mCondition=NS_ERROR_UNEXPECTED;}if(mPollFlags==PR_POLL_EXCEPT)mPollFlags=0;// make idle}// called on the socket thread onlyvoidnsSocketTransport::OnSocketDetached(PRFileDesc*fd){SOCKET_LOG(("nsSocketTransport::OnSocketDetached [this=%p cond=%"PRIx32"]\n",this,static_cast<uint32_t>(mCondition)));MOZ_ASSERT(OnSocketThread(),"not on socket thread");// if we didn't initiate this detach, then be sure to pass an error// condition up to our consumers. (e.g., STS is shutting down.)if(NS_SUCCEEDED(mCondition)){if(gIOService->IsOffline()){mCondition=NS_ERROR_OFFLINE;}else{mCondition=NS_ERROR_ABORT;}}mFastOpenLayerHasBufferedData=false;// If we are not shutting down try again.if(!gIOService->IsNetTearingDown()&&RecoverFromError())mCondition=NS_OK;else{mState=STATE_CLOSED;// The error can happened before we start fast open. In that case do not// call mFastOpenCallback->SetFastOpenConnected; If error happends during// fast open, inform the halfOpenSocket.// If we cancel the connection because backup socket was successfully// connected, mFDFastOpenInProgress will be true but mFastOpenCallback// will be nullptr.if(mFDFastOpenInProgress&&mFastOpenCallback){mFastOpenCallback->SetFastOpenConnected(mCondition,false);}mFastOpenCallback=nullptr;// make sure there isn't any pending DNS requestif(mDNSRequest){mDNSRequest->Cancel(NS_ERROR_ABORT);mDNSRequest=nullptr;}//// notify input/output streams//mInput.OnSocketReady(mCondition);mOutput.OnSocketReady(mCondition);}// If FastOpen has been used (mFDFastOpenInProgress==true),// mFastOpenCallback must be nullptr now. We decided to recover from// error like NET_TIMEOUT, CONNECTION_REFUSED or we have called// SetFastOpenConnected(mCondition) in this function a couple of lines// above.// If FastOpen has not been used (mFDFastOpenInProgress==false) it can be// that mFastOpenCallback is no null, this is the case when we recover from// errors like UKNOWN_HOST in which case socket was not been connected yet// and mFastOpenCallback-StartFastOpen was not be called yet (but we can// still call it in the next try).MOZ_ASSERT(!(mFDFastOpenInProgress&&mFastOpenCallback));// break any potential reference cycle between the security info object// and ourselves by resetting its notification callbacks object. see// bug 285991 for details.nsCOMPtr<nsISSLSocketControl>secCtrl=do_QueryInterface(mSecInfo);if(secCtrl)secCtrl->SetNotificationCallbacks(nullptr);// finally, release our reference to the socket (must do this within// the transport lock) possibly closing the socket. Also release our// listeners to break potential refcount cycles.// We should be careful not to release mEventSink and mCallbacks while// we're locked, because releasing it might require acquiring the lock// again, so we just null out mEventSink and mCallbacks while we're// holding the lock, and let the stack based objects' destuctors take// care of destroying it if needed.nsCOMPtr<nsIInterfaceRequestor>ourCallbacks;nsCOMPtr<nsITransportEventSink>ourEventSink;{MutexAutoLocklock(mLock);if(mFD.IsInitialized()){ReleaseFD_Locked(mFD);// flag mFD as unusable; this prevents other consumers from // acquiring a reference to mFD.mFDconnected=false;mFDFastOpenInProgress=false;}// We must release mCallbacks and mEventSink to avoid memory leak// but only when RecoverFromError() above failed. Otherwise we lose// link with UI and security callbacks on next connection attempt // round. That would lead e.g. to a broken certificate exception page.if(NS_FAILED(mCondition)){mCallbacks.swap(ourCallbacks);mEventSink.swap(ourEventSink);}}}voidnsSocketTransport::IsLocal(bool*aIsLocal){{MutexAutoLocklock(mLock);#if defined(XP_UNIX)// Unix-domain sockets are always local.if(mNetAddr.raw.family==PR_AF_LOCAL){*aIsLocal=true;return;}#endif*aIsLocal=IsLoopBackAddress(&mNetAddr);}}//-----------------------------------------------------------------------------// xpcom apiNS_IMPL_ISUPPORTS(nsSocketTransport,nsISocketTransport,nsITransport,nsIDNSListener,nsIClassInfo,nsIInterfaceRequestor)NS_IMPL_CI_INTERFACE_GETTER(nsSocketTransport,nsISocketTransport,nsITransport,nsIDNSListener,nsIInterfaceRequestor)NS_IMETHODIMPnsSocketTransport::OpenInputStream(uint32_tflags,uint32_tsegsize,uint32_tsegcount,nsIInputStream**result){SOCKET_LOG(("nsSocketTransport::OpenInputStream [this=%p flags=%x]\n",this,flags));NS_ENSURE_TRUE(!mInput.IsReferenced(),NS_ERROR_UNEXPECTED);nsresultrv;nsCOMPtr<nsIAsyncInputStream>pipeIn;if(!(flags&OPEN_UNBUFFERED)||(flags&OPEN_BLOCKING)){// XXX if the caller wants blocking, then the caller also gets buffered!//bool openBuffered = !(flags & OPEN_UNBUFFERED);boolopenBlocking=(flags&OPEN_BLOCKING);net_ResolveSegmentParams(segsize,segcount);// create a pipensCOMPtr<nsIAsyncOutputStream>pipeOut;rv=NS_NewPipe2(getter_AddRefs(pipeIn),getter_AddRefs(pipeOut),!openBlocking,true,segsize,segcount);if(NS_FAILED(rv))returnrv;// async copy from socket to piperv=NS_AsyncCopy(&mInput,pipeOut,mSocketTransportService,NS_ASYNCCOPY_VIA_WRITESEGMENTS,segsize);if(NS_FAILED(rv))returnrv;*result=pipeIn;}else*result=&mInput;// flag input stream as openmInputClosed=false;rv=PostEvent(MSG_ENSURE_CONNECT);if(NS_FAILED(rv))returnrv;NS_ADDREF(*result);returnNS_OK;}NS_IMETHODIMPnsSocketTransport::OpenOutputStream(uint32_tflags,uint32_tsegsize,uint32_tsegcount,nsIOutputStream**result){SOCKET_LOG(("nsSocketTransport::OpenOutputStream [this=%p flags=%x]\n",this,flags));NS_ENSURE_TRUE(!mOutput.IsReferenced(),NS_ERROR_UNEXPECTED);nsresultrv;nsCOMPtr<nsIAsyncOutputStream>pipeOut;if(!(flags&OPEN_UNBUFFERED)||(flags&OPEN_BLOCKING)){// XXX if the caller wants blocking, then the caller also gets buffered!//bool openBuffered = !(flags & OPEN_UNBUFFERED);boolopenBlocking=(flags&OPEN_BLOCKING);net_ResolveSegmentParams(segsize,segcount);// create a pipensCOMPtr<nsIAsyncInputStream>pipeIn;rv=NS_NewPipe2(getter_AddRefs(pipeIn),getter_AddRefs(pipeOut),true,!openBlocking,segsize,segcount);if(NS_FAILED(rv))returnrv;// async copy from socket to piperv=NS_AsyncCopy(pipeIn,&mOutput,mSocketTransportService,NS_ASYNCCOPY_VIA_READSEGMENTS,segsize);if(NS_FAILED(rv))returnrv;*result=pipeOut;}else*result=&mOutput;// flag output stream as openmOutputClosed=false;rv=PostEvent(MSG_ENSURE_CONNECT);if(NS_FAILED(rv))returnrv;NS_ADDREF(*result);returnNS_OK;}NS_IMETHODIMPnsSocketTransport::Close(nsresultreason){if(NS_SUCCEEDED(reason))reason=NS_BASE_STREAM_CLOSED;mDoNotRetryToConnect=true;if(mFDFastOpenInProgress&&mFastOpenCallback){mFastOpenCallback->SetFastOpenConnected(reason,false);}mFastOpenCallback=nullptr;mInput.CloseWithStatus(reason);mOutput.CloseWithStatus(reason);returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetSecurityInfo(nsISupports**secinfo){MutexAutoLocklock(mLock);NS_IF_ADDREF(*secinfo=mSecInfo);returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor**callbacks){MutexAutoLocklock(mLock);NS_IF_ADDREF(*callbacks=mCallbacks);returnNS_OK;}NS_IMETHODIMPnsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor*callbacks){nsCOMPtr<nsIInterfaceRequestor>threadsafeCallbacks;NS_NewNotificationCallbacksAggregation(callbacks,nullptr,GetCurrentThreadEventTarget(),getter_AddRefs(threadsafeCallbacks));nsCOMPtr<nsISupports>secinfo;{MutexAutoLocklock(mLock);mCallbacks=threadsafeCallbacks;SOCKET_LOG(("Reset callbacks for secinfo=%p callbacks=%p\n",mSecInfo.get(),mCallbacks.get()));secinfo=mSecInfo;}// don't call into PSM while holding mLock!!nsCOMPtr<nsISSLSocketControl>secCtrl(do_QueryInterface(secinfo));if(secCtrl)secCtrl->SetNotificationCallbacks(threadsafeCallbacks);returnNS_OK;}NS_IMETHODIMPnsSocketTransport::SetEventSink(nsITransportEventSink*sink,nsIEventTarget*target){nsCOMPtr<nsITransportEventSink>temp;if(target){nsresultrv=net_NewTransportEventSinkProxy(getter_AddRefs(temp),sink,target);if(NS_FAILED(rv))returnrv;sink=temp.get();}MutexAutoLocklock(mLock);mEventSink=sink;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::IsAlive(bool*result){*result=false;// During Fast Open we need to return true here.if(mFDFastOpenInProgress){*result=true;returnNS_OK;}nsresultconditionWhileLocked=NS_OK;PRFileDescAutoLockfd(this,false,&conditionWhileLocked);if(NS_FAILED(conditionWhileLocked)||!fd.IsInitialized()){returnNS_OK;}// XXX do some idle-time based checks??charc;int32_trval=PR_Recv(fd,&c,1,PR_MSG_PEEK,0);if((rval>0)||(rval<0&&PR_GetError()==PR_WOULD_BLOCK_ERROR))*result=true;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetHost(nsACString&host){host=SocketHost();returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetPort(int32_t*port){*port=(int32_t)SocketPort();returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetNetworkInterfaceId(nsACString&aNetworkInterfaceId){MOZ_ASSERT(OnSocketThread(),"not on socket thread");aNetworkInterfaceId=mNetworkInterfaceId;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::SetNetworkInterfaceId(constnsACString&aNetworkInterfaceId){MOZ_ASSERT(OnSocketThread(),"not on socket thread");mNetworkInterfaceId=aNetworkInterfaceId;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetScriptableOriginAttributes(JSContext*aCx,JS::MutableHandle<JS::Value>aOriginAttributes){if(NS_WARN_IF(!ToJSValue(aCx,mOriginAttributes,aOriginAttributes))){returnNS_ERROR_FAILURE;}returnNS_OK;}NS_IMETHODIMPnsSocketTransport::SetScriptableOriginAttributes(JSContext*aCx,JS::Handle<JS::Value>aOriginAttributes){MutexAutoLocklock(mLock);NS_ENSURE_FALSE(mFD.IsInitialized(),NS_ERROR_FAILURE);OriginAttributesattrs;if(!aOriginAttributes.isObject()||!attrs.Init(aCx,aOriginAttributes)){returnNS_ERROR_INVALID_ARG;}mOriginAttributes=attrs;returnNS_OK;}nsresultnsSocketTransport::GetOriginAttributes(OriginAttributes*aOriginAttributes){NS_ENSURE_ARG(aOriginAttributes);*aOriginAttributes=mOriginAttributes;returnNS_OK;}nsresultnsSocketTransport::SetOriginAttributes(constOriginAttributes&aOriginAttributes){MutexAutoLocklock(mLock);NS_ENSURE_FALSE(mFD.IsInitialized(),NS_ERROR_FAILURE);mOriginAttributes=aOriginAttributes;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetPeerAddr(NetAddr*addr){// once we are in the connected state, mNetAddr will not change.// so if we can verify that we are in the connected state, then// we can freely access mNetAddr from any thread without being// inside a critical section.if(!mNetAddrIsSet){SOCKET_LOG(("nsSocketTransport::GetPeerAddr [this=%p state=%d] ""NOT_AVAILABLE because not yet connected.",this,mState));returnNS_ERROR_NOT_AVAILABLE;}memcpy(addr,&mNetAddr,sizeof(NetAddr));returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetSelfAddr(NetAddr*addr){// once we are in the connected state, mSelfAddr will not change.// so if we can verify that we are in the connected state, then// we can freely access mSelfAddr from any thread without being// inside a critical section.if(!mSelfAddrIsSet){SOCKET_LOG(("nsSocketTransport::GetSelfAddr [this=%p state=%d] ""NOT_AVAILABLE because not yet connected.",this,mState));returnNS_ERROR_NOT_AVAILABLE;}memcpy(addr,&mSelfAddr,sizeof(NetAddr));returnNS_OK;}NS_IMETHODIMPnsSocketTransport::Bind(NetAddr*aLocalAddr){NS_ENSURE_ARG(aLocalAddr);MutexAutoLocklock(mLock);if(mAttached){returnNS_ERROR_FAILURE;}mBindAddr=newNetAddr();memcpy(mBindAddr.get(),aLocalAddr,sizeof(NetAddr));returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetScriptablePeerAddr(nsINetAddr**addr){NetAddrrawAddr;nsresultrv;rv=GetPeerAddr(&rawAddr);if(NS_FAILED(rv))returnrv;NS_ADDREF(*addr=newnsNetAddr(&rawAddr));returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetScriptableSelfAddr(nsINetAddr**addr){NetAddrrawAddr;nsresultrv;rv=GetSelfAddr(&rawAddr);if(NS_FAILED(rv))returnrv;NS_ADDREF(*addr=newnsNetAddr(&rawAddr));returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetTimeout(uint32_ttype,uint32_t*value){NS_ENSURE_ARG_MAX(type,nsISocketTransport::TIMEOUT_READ_WRITE);*value=(uint32_t)mTimeouts[type];returnNS_OK;}NS_IMETHODIMPnsSocketTransport::SetTimeout(uint32_ttype,uint32_tvalue){NS_ENSURE_ARG_MAX(type,nsISocketTransport::TIMEOUT_READ_WRITE);// truncate overly large timeout values.mTimeouts[type]=(uint16_t)std::min<uint32_t>(value,UINT16_MAX);PostEvent(MSG_TIMEOUT_CHANGED);returnNS_OK;}NS_IMETHODIMPnsSocketTransport::SetReuseAddrPort(boolreuseAddrPort){mReuseAddrPort=reuseAddrPort;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::SetQoSBits(uint8_taQoSBits){// Don't do any checking here of bits. Why? Because as of RFC-4594// several different Class Selector and Assured Forwarding values// have been defined, but that isn't to say more won't be added later.// In that case, any checking would be an impediment to interoperating// with newer QoS definitions.mQoSBits=aQoSBits;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetQoSBits(uint8_t*aQoSBits){*aQoSBits=mQoSBits;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetRecvBufferSize(uint32_t*aSize){PRFileDescAutoLockfd(this,false);if(!fd.IsInitialized())returnNS_ERROR_NOT_CONNECTED;nsresultrv=NS_OK;PRSocketOptionDataopt;opt.option=PR_SockOpt_RecvBufferSize;if(PR_GetSocketOption(fd,&opt)==PR_SUCCESS)*aSize=opt.value.recv_buffer_size;elserv=NS_ERROR_FAILURE;returnrv;}NS_IMETHODIMPnsSocketTransport::GetSendBufferSize(uint32_t*aSize){PRFileDescAutoLockfd(this,false);if(!fd.IsInitialized())returnNS_ERROR_NOT_CONNECTED;nsresultrv=NS_OK;PRSocketOptionDataopt;opt.option=PR_SockOpt_SendBufferSize;if(PR_GetSocketOption(fd,&opt)==PR_SUCCESS)*aSize=opt.value.send_buffer_size;elserv=NS_ERROR_FAILURE;returnrv;}NS_IMETHODIMPnsSocketTransport::SetRecvBufferSize(uint32_taSize){PRFileDescAutoLockfd(this,false);if(!fd.IsInitialized())returnNS_ERROR_NOT_CONNECTED;nsresultrv=NS_OK;PRSocketOptionDataopt;opt.option=PR_SockOpt_RecvBufferSize;opt.value.recv_buffer_size=aSize;if(PR_SetSocketOption(fd,&opt)!=PR_SUCCESS)rv=NS_ERROR_FAILURE;returnrv;}NS_IMETHODIMPnsSocketTransport::SetSendBufferSize(uint32_taSize){PRFileDescAutoLockfd(this,false);if(!fd.IsInitialized())returnNS_ERROR_NOT_CONNECTED;nsresultrv=NS_OK;PRSocketOptionDataopt;opt.option=PR_SockOpt_SendBufferSize;opt.value.send_buffer_size=aSize;if(PR_SetSocketOption(fd,&opt)!=PR_SUCCESS)rv=NS_ERROR_FAILURE;returnrv;}NS_IMETHODIMPnsSocketTransport::OnLookupComplete(nsICancelable*request,nsIDNSRecord*rec,nsresultstatus){// flag host lookup complete for the benefit of the ResolveHost method.mResolving=false;nsresultrv=PostEvent(MSG_DNS_LOOKUP_COMPLETE,status,rec);// if posting a message fails, then we should assume that the socket// transport has been shutdown. this should never happen! if it does// it means that the socket transport service was shutdown before the// DNS service.if(NS_FAILED(rv))NS_WARNING("unable to post DNS lookup complete message");returnNS_OK;}// nsIInterfaceRequestorNS_IMETHODIMPnsSocketTransport::GetInterface(constnsIID&iid,void**result){if(iid.Equals(NS_GET_IID(nsIDNSRecord))){returnmDNSRecord?mDNSRecord->QueryInterface(iid,result):NS_ERROR_NO_INTERFACE;}returnthis->QueryInterface(iid,result);}NS_IMETHODIMPnsSocketTransport::GetInterfaces(uint32_t*count,nsIID***array){returnNS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(count,array);}NS_IMETHODIMPnsSocketTransport::GetScriptableHelper(nsIXPCScriptable**_retval){*_retval=nullptr;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetContractID(char**aContractID){*aContractID=nullptr;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetClassDescription(char**aClassDescription){*aClassDescription=nullptr;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetClassID(nsCID**aClassID){*aClassID=nullptr;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetFlags(uint32_t*aFlags){*aFlags=nsIClassInfo::THREADSAFE;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetClassIDNoAlloc(nsCID*aClassIDNoAlloc){returnNS_ERROR_NOT_AVAILABLE;}NS_IMETHODIMPnsSocketTransport::GetConnectionFlags(uint32_t*value){*value=mConnectionFlags;returnNS_OK;}NS_IMETHODIMPnsSocketTransport::SetConnectionFlags(uint32_tvalue){mConnectionFlags=value;mIsPrivate=value&nsISocketTransport::NO_PERMANENT_STORAGE;returnNS_OK;}voidnsSocketTransport::OnKeepaliveEnabledPrefChange(boolaEnabled){MOZ_ASSERT(OnSocketThread(),"not on socket thread");// The global pref toggles keepalive as a system feature; it only affects// an individual socket if keepalive has been specifically enabled for it.// So, ensure keepalive is configured correctly if previously enabled.if(mKeepaliveEnabled){nsresultrv=SetKeepaliveEnabledInternal(aEnabled);if(NS_WARN_IF(NS_FAILED(rv))){SOCKET_LOG((" SetKeepaliveEnabledInternal [%s] failed rv[0x%"PRIx32"]",aEnabled?"enable":"disable",static_cast<uint32_t>(rv)));}}}nsresultnsSocketTransport::SetKeepaliveEnabledInternal(boolaEnable){MOZ_ASSERT(mKeepaliveIdleTimeS>0&&mKeepaliveIdleTimeS<=kMaxTCPKeepIdle);MOZ_ASSERT(mKeepaliveRetryIntervalS>0&&mKeepaliveRetryIntervalS<=kMaxTCPKeepIntvl);MOZ_ASSERT(mKeepaliveProbeCount>0&&mKeepaliveProbeCount<=kMaxTCPKeepCount);PRFileDescAutoLockfd(this,true);if(NS_WARN_IF(!fd.IsInitialized())){returnNS_ERROR_NOT_INITIALIZED;}// Only enable if keepalives are globally enabled, but ensure other// options are set correctly on the fd.boolenable=aEnable&&mSocketTransportService->IsKeepaliveEnabled();nsresultrv=fd.SetKeepaliveVals(enable,mKeepaliveIdleTimeS,mKeepaliveRetryIntervalS,mKeepaliveProbeCount);if(NS_WARN_IF(NS_FAILED(rv))){SOCKET_LOG((" SetKeepaliveVals failed rv[0x%"PRIx32"]",static_cast<uint32_t>(rv)));returnrv;}rv=fd.SetKeepaliveEnabled(enable);if(NS_WARN_IF(NS_FAILED(rv))){SOCKET_LOG((" SetKeepaliveEnabled failed rv[0x%"PRIx32"]",static_cast<uint32_t>(rv)));returnrv;}returnNS_OK;}NS_IMETHODIMPnsSocketTransport::GetKeepaliveEnabled(bool*aResult){MOZ_ASSERT(aResult);*aResult=mKeepaliveEnabled;returnNS_OK;}nsresultnsSocketTransport::EnsureKeepaliveValsAreInitialized(){nsresultrv=NS_OK;int32_tval=-1;if(mKeepaliveIdleTimeS==-1){rv=mSocketTransportService->GetKeepaliveIdleTime(&val);if(NS_WARN_IF(NS_FAILED(rv))){returnrv;}mKeepaliveIdleTimeS=val;}if(mKeepaliveRetryIntervalS==-1){rv=mSocketTransportService->GetKeepaliveRetryInterval(&val);if(NS_WARN_IF(NS_FAILED(rv))){returnrv;}mKeepaliveRetryIntervalS=val;}if(mKeepaliveProbeCount==-1){rv=mSocketTransportService->GetKeepaliveProbeCount(&val);if(NS_WARN_IF(NS_FAILED(rv))){returnrv;}mKeepaliveProbeCount=val;}returnNS_OK;}NS_IMETHODIMPnsSocketTransport::SetKeepaliveEnabled(boolaEnable){#if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)MOZ_ASSERT(OnSocketThread(),"not on socket thread");if(aEnable==mKeepaliveEnabled){SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] already %s.",this,aEnable?"enabled":"disabled"));returnNS_OK;}nsresultrv=NS_OK;if(aEnable){rv=EnsureKeepaliveValsAreInitialized();if(NS_WARN_IF(NS_FAILED(rv))){SOCKET_LOG((" SetKeepaliveEnabled [%p] ""error [0x%"PRIx32"] initializing keepalive vals",this,static_cast<uint32_t>(rv)));returnrv;}}SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] ""%s, idle time[%ds] retry interval[%ds] packet count[%d]: ""globally %s.",this,aEnable?"enabled":"disabled",mKeepaliveIdleTimeS,mKeepaliveRetryIntervalS,mKeepaliveProbeCount,mSocketTransportService->IsKeepaliveEnabled()?"enabled":"disabled"));// Set mKeepaliveEnabled here so that state is maintained; it is possible// that we're in between fds, e.g. the 1st IP address failed, so we're about// to retry on a 2nd from the DNS record.mKeepaliveEnabled=aEnable;rv=SetKeepaliveEnabledInternal(aEnable);if(NS_WARN_IF(NS_FAILED(rv))){SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%"PRIx32"]",static_cast<uint32_t>(rv)));returnrv;}returnNS_OK;#else /* !(defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)) */SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled unsupported platform"));returnNS_ERROR_NOT_IMPLEMENTED;#endif}NS_IMETHODIMPnsSocketTransport::SetKeepaliveVals(int32_taIdleTime,int32_taRetryInterval){#if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)MOZ_ASSERT(OnSocketThread(),"not on socket thread");if(NS_WARN_IF(aIdleTime<=0||kMaxTCPKeepIdle<aIdleTime)){returnNS_ERROR_INVALID_ARG;}if(NS_WARN_IF(aRetryInterval<=0||kMaxTCPKeepIntvl<aRetryInterval)){returnNS_ERROR_INVALID_ARG;}if(aIdleTime==mKeepaliveIdleTimeS&&aRetryInterval==mKeepaliveRetryIntervalS){SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals [%p] idle time ""already %ds and retry interval already %ds.",this,mKeepaliveIdleTimeS,mKeepaliveRetryIntervalS));returnNS_OK;}mKeepaliveIdleTimeS=aIdleTime;mKeepaliveRetryIntervalS=aRetryInterval;nsresultrv=NS_OK;if(mKeepaliveProbeCount==-1){int32_tval=-1;nsresultrv=mSocketTransportService->GetKeepaliveProbeCount(&val);if(NS_WARN_IF(NS_FAILED(rv))){returnrv;}mKeepaliveProbeCount=val;}SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals [%p] ""keepalive %s, idle time[%ds] retry interval[%ds] ""packet count[%d]",this,mKeepaliveEnabled?"enabled":"disabled",mKeepaliveIdleTimeS,mKeepaliveRetryIntervalS,mKeepaliveProbeCount));PRFileDescAutoLockfd(this,true);if(NS_WARN_IF(!fd.IsInitialized())){returnNS_ERROR_NULL_POINTER;}rv=fd.SetKeepaliveVals(mKeepaliveEnabled,mKeepaliveIdleTimeS,mKeepaliveRetryIntervalS,mKeepaliveProbeCount);if(NS_WARN_IF(NS_FAILED(rv))){returnrv;}returnNS_OK;#elseSOCKET_LOG(("nsSocketTransport::SetKeepaliveVals unsupported platform"));returnNS_ERROR_NOT_IMPLEMENTED;#endif}#ifdef ENABLE_SOCKET_TRACING#include<stdio.h>#include<ctype.h>#include"prenv.h"staticvoidDumpBytesToFile(constchar*path,constchar*header,constchar*buf,int32_tn){FILE*fp=fopen(path,"a");fprintf(fp,"\n%s [%d bytes]\n",header,n);constunsignedchar*p;while(n){p=(constunsignedchar*)buf;int32_ti,row_max=std::min(16,n);for(i=0;i<row_max;++i)fprintf(fp,"%02x ",*p++);for(i=row_max;i<16;++i)fprintf(fp," ");p=(constunsignedchar*)buf;for(i=0;i<row_max;++i,++p){if(isprint(*p))fprintf(fp,"%c",*p);elsefprintf(fp,".");}fprintf(fp,"\n");buf+=row_max;n-=row_max;}fprintf(fp,"\n");fclose(fp);}voidnsSocketTransport::TraceInBuf(constchar*buf,int32_tn){char*val=PR_GetEnv("NECKO_SOCKET_TRACE_LOG");if(!val||!*val)return;nsAutoCStringheader;header.AssignLiteral("Reading from: ");header.Append(mHost);header.Append(':');header.AppendInt(mPort);DumpBytesToFile(val,header.get(),buf,n);}voidnsSocketTransport::TraceOutBuf(constchar*buf,int32_tn){char*val=PR_GetEnv("NECKO_SOCKET_TRACE_LOG");if(!val||!*val)return;nsAutoCStringheader;header.AssignLiteral("Writing to: ");header.Append(mHost);header.Append(':');header.AppendInt(mPort);DumpBytesToFile(val,header.get(),buf,n);}#endifstaticvoidLogNSPRError(constchar*aPrefix,constvoid*aObjPtr){#if defined(DEBUG)PRErrorCodeerrCode=PR_GetError();interrLen=PR_GetErrorTextLength();nsAutoCStringerrStr;if(errLen>0){errStr.SetLength(errLen);PR_GetErrorText(errStr.BeginWriting());}NS_WARNING(nsPrintfCString("%s [%p] NSPR error[0x%x] %s.",aPrefix?aPrefix:"nsSocketTransport",aObjPtr,errCode,errLen>0?errStr.BeginReading():"<no error text>").get());#endif}nsresultnsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled(boolaEnable){MOZ_ASSERT(OnSocketThread(),"not on socket thread");MOZ_ASSERT(!(aEnable&&!gSocketTransportService->IsKeepaliveEnabled()),"Cannot enable keepalive if global pref is disabled!");if(aEnable&&!gSocketTransportService->IsKeepaliveEnabled()){returnNS_ERROR_ILLEGAL_VALUE;}PRSocketOptionDataopt;opt.option=PR_SockOpt_Keepalive;opt.value.keep_alive=aEnable;PRStatusstatus=PR_SetSocketOption(mFd,&opt);if(NS_WARN_IF(status!=PR_SUCCESS)){LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled",mSocketTransport);returnErrorAccordingToNSPR(PR_GetError());}returnNS_OK;}staticvoidLogOSError(constchar*aPrefix,constvoid*aObjPtr){#if defined(DEBUG)MOZ_ASSERT(OnSocketThread(),"not on socket thread");#ifdef XP_WINDWORDerrCode=WSAGetLastError();LPVOIDerrMessage;FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,NULL,errCode,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&errMessage,0,NULL);#elseinterrCode=errno;char*errMessage=strerror(errno);#endifNS_WARNING(nsPrintfCString("%s [%p] OS error[0x%x] %s",aPrefix?aPrefix:"nsSocketTransport",aObjPtr,errCode,errMessage?errMessage:"<no error text>").get());#ifdef XP_WINLocalFree(errMessage);#endif#endif}/* XXX PR_SetSockOpt does not support setting keepalive values, so native * handles and platform specific apis (setsockopt, WSAIOCtl) are used in this * file. Requires inclusion of NSPR private/pprio.h, and platform headers. */nsresultnsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(boolaEnabled,intaIdleTime,intaRetryInterval,intaProbeCount){#if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)MOZ_ASSERT(OnSocketThread(),"not on socket thread");if(NS_WARN_IF(aIdleTime<=0||kMaxTCPKeepIdle<aIdleTime)){returnNS_ERROR_INVALID_ARG;}if(NS_WARN_IF(aRetryInterval<=0||kMaxTCPKeepIntvl<aRetryInterval)){returnNS_ERROR_INVALID_ARG;}if(NS_WARN_IF(aProbeCount<=0||kMaxTCPKeepCount<aProbeCount)){returnNS_ERROR_INVALID_ARG;}PROsfdsock=PR_FileDesc2NativeHandle(mFd);if(NS_WARN_IF(sock==-1)){LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals",mSocketTransport);returnErrorAccordingToNSPR(PR_GetError());}#endif#if defined(XP_WIN)// Windows allows idle time and retry interval to be set; NOT ping count.structtcp_keepalivekeepalive_vals={(u_long)aEnabled,// Windows uses msec.(u_long)(aIdleTime*1000UL),(u_long)(aRetryInterval*1000UL)};DWORDbytes_returned;interr=WSAIoctl(sock,SIO_KEEPALIVE_VALS,&keepalive_vals,sizeof(keepalive_vals),NULL,0,&bytes_returned,NULL,NULL);if(NS_WARN_IF(err)){LogOSError("nsSocketTransport WSAIoctl failed",mSocketTransport);returnNS_ERROR_UNEXPECTED;}returnNS_OK;#elif defined(XP_DARWIN)// Darwin uses sec; only supports idle time being set.interr=setsockopt(sock,IPPROTO_TCP,TCP_KEEPALIVE,&aIdleTime,sizeof(aIdleTime));if(NS_WARN_IF(err)){LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",mSocketTransport);returnNS_ERROR_UNEXPECTED;}returnNS_OK;#elif defined(XP_UNIX)// Not all *nix OSes support the following setsockopt() options// ... but we assume they are supported in the Android kernel;// build errors will tell us if they are not.#if defined(ANDROID) || defined(TCP_KEEPIDLE)// Idle time until first keepalive probe; interval between ack'd probes; seconds.interr=setsockopt(sock,IPPROTO_TCP,TCP_KEEPIDLE,&aIdleTime,sizeof(aIdleTime));if(NS_WARN_IF(err)){LogOSError("nsSocketTransport Failed setting TCP_KEEPIDLE",mSocketTransport);returnNS_ERROR_UNEXPECTED;}#endif#if defined(ANDROID) || defined(TCP_KEEPINTVL)// Interval between unack'd keepalive probes; seconds.err=setsockopt(sock,IPPROTO_TCP,TCP_KEEPINTVL,&aRetryInterval,sizeof(aRetryInterval));if(NS_WARN_IF(err)){LogOSError("nsSocketTransport Failed setting TCP_KEEPINTVL",mSocketTransport);returnNS_ERROR_UNEXPECTED;}#endif#if defined(ANDROID) || defined(TCP_KEEPCNT)// Number of unack'd keepalive probes before connection times out.err=setsockopt(sock,IPPROTO_TCP,TCP_KEEPCNT,&aProbeCount,sizeof(aProbeCount));if(NS_WARN_IF(err)){LogOSError("nsSocketTransport Failed setting TCP_KEEPCNT",mSocketTransport);returnNS_ERROR_UNEXPECTED;}#endifreturnNS_OK;#elseMOZ_ASSERT(false,"nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals ""called on unsupported platform!");returnNS_ERROR_UNEXPECTED;#endif}voidnsSocketTransport::CloseSocket(PRFileDesc*aFd,boolaTelemetryEnabled){#if defined(XP_WIN)AttachShutdownLayer(aFd);#endif// We use PRIntervalTime here because we need// nsIOService::LastOfflineStateChange time and// nsIOService::LastConectivityChange time to be atomic.PRIntervalTimecloseStarted;if(aTelemetryEnabled){closeStarted=PR_IntervalNow();}PR_Close(aFd);if(aTelemetryEnabled){SendPRBlockingTelemetry(closeStarted,Telemetry::PRCLOSE_TCP_BLOCKING_TIME_NORMAL,Telemetry::PRCLOSE_TCP_BLOCKING_TIME_SHUTDOWN,Telemetry::PRCLOSE_TCP_BLOCKING_TIME_CONNECTIVITY_CHANGE,Telemetry::PRCLOSE_TCP_BLOCKING_TIME_LINK_CHANGE,Telemetry::PRCLOSE_TCP_BLOCKING_TIME_OFFLINE);}}voidnsSocketTransport::SendPRBlockingTelemetry(PRIntervalTimeaStart,Telemetry::HistogramIDaIDNormal,Telemetry::HistogramIDaIDShutdown,Telemetry::HistogramIDaIDConnectivityChange,Telemetry::HistogramIDaIDLinkChange,Telemetry::HistogramIDaIDOffline){PRIntervalTimenow=PR_IntervalNow();if(gIOService->IsNetTearingDown()){Telemetry::Accumulate(aIDShutdown,PR_IntervalToMilliseconds(now-aStart));}elseif(PR_IntervalToSeconds(now-gIOService->LastConnectivityChange())<60){Telemetry::Accumulate(aIDConnectivityChange,PR_IntervalToMilliseconds(now-aStart));}elseif(PR_IntervalToSeconds(now-gIOService->LastNetworkLinkChange())<60){Telemetry::Accumulate(aIDLinkChange,PR_IntervalToMilliseconds(now-aStart));}elseif(PR_IntervalToSeconds(now-gIOService->LastOfflineStateChange())<60){Telemetry::Accumulate(aIDOffline,PR_IntervalToMilliseconds(now-aStart));}else{Telemetry::Accumulate(aIDNormal,PR_IntervalToMilliseconds(now-aStart));}}NS_IMETHODIMPnsSocketTransport::SetFastOpenCallback(TCPFastOpen*aFastOpen){mFastOpenCallback=aFastOpen;returnNS_OK;}}// namespace net}// namespace mozilla